gtkcssprovider.c 93.9 KB
Newer Older
Carlos Garnacho's avatar
Carlos Garnacho committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/* GTK - The GIMP Toolkit
 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <string.h>
#include <stdlib.h>
24

25
#include <gdk-pixbuf/gdk-pixbuf.h>
26
#include <cairo-gobject.h>
Carlos Garnacho's avatar
Carlos Garnacho committed
27

28 29
#include "gtkcssproviderprivate.h"

Benjamin Otte's avatar
Benjamin Otte committed
30
#include "gtkcssparserprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
31
#include "gtkcssselectorprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
32
#include "gtksymboliccolor.h"
33 34
#include "gtkstyleprovider.h"
#include "gtkstylecontextprivate.h"
35
#include "gtkstylepropertiesprivate.h"
36
#include "gtkstylepropertyprivate.h"
37
#include "gtkbindings.h"
38
#include "gtkmarshalers.h"
39
#include "gtkprivate.h"
40
#include "gtkintl.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
41

Carlos Garnacho's avatar
Carlos Garnacho committed
42 43 44 45 46 47
/**
 * SECTION:gtkcssprovider
 * @Short_description: CSS-like styling for widgets
 * @Title: GtkCssProvider
 * @See_also: #GtkStyleContext, #GtkStyleProvider
 *
Matthias Clasen's avatar
Matthias Clasen committed
48 49 50
 * 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.
Carlos Garnacho's avatar
Carlos Garnacho committed
51
 *
Matthias Clasen's avatar
Matthias Clasen committed
52 53 54 55 56 57 58
 * <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,
59
 * the file <filename><envar>$XDG_CONFIG_HOME</envar>/gtk-3.0/gtk.css</filename>
Matthias Clasen's avatar
Matthias Clasen committed
60
 * is loaded if it exists. Then, GTK+ tries to load
61
 * <filename><envar>$HOME</envar>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
Matthias Clasen's avatar
Matthias Clasen committed
62
 * falling back to
63
 * <filename><replaceable>datadir</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
Matthias Clasen's avatar
Matthias Clasen committed
64
 * where <replaceable>theme-name</replaceable> is the name of the current theme
65
 * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>datadir</replaceable>
Matthias Clasen's avatar
Matthias Clasen committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
 * 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>
Carlos Garnacho's avatar
Carlos Garnacho committed
93
 * <refsect2 id="gtkcssprovider-selectors">
Matthias Clasen's avatar
Matthias Clasen committed
94
 * <title>Selectors</title>
Carlos Garnacho's avatar
Carlos Garnacho committed
95
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
 * 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>
 * 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>
Carlos Garnacho's avatar
Carlos Garnacho committed
120 121
 * </para>
 * <example>
Matthias Clasen's avatar
Matthias Clasen committed
122 123
 * <title>Widget classes and names in selectors</title>
 * <programlisting language="text">
Carlos Garnacho's avatar
Carlos Garnacho committed
124 125
 * /&ast; Theme labels that are descendants of a window &ast;/
 * GtkWindow GtkLabel {
Matthias Clasen's avatar
Matthias Clasen committed
126
 *     background-color: &num;898989
Carlos Garnacho's avatar
Carlos Garnacho committed
127 128 129 130
 * }
 *
 * /&ast; Theme notebooks, and anything that's within these &ast;/
 * GtkNotebook {
Matthias Clasen's avatar
Matthias Clasen committed
131
 *     background-color: &num;a939f0
Carlos Garnacho's avatar
Carlos Garnacho committed
132 133 134 135 136 137
 * }
 *
 * /&ast; Theme combo boxes, and entries that
 *  are direct children of a notebook &ast;/
 * GtkComboBox,
 * GtkNotebook > GtkEntry {
Matthias Clasen's avatar
Matthias Clasen committed
138 139
 *     color: @fg_color;
 *     background-color: &num;1209a2
Carlos Garnacho's avatar
Carlos Garnacho committed
140 141 142 143
 * }
 *
 * /&ast; Theme any widget within a GtkBin &ast;/
 * GtkBin * {
Matthias Clasen's avatar
Matthias Clasen committed
144
 *     font-name: Sans 20
Carlos Garnacho's avatar
Carlos Garnacho committed
145
 * }
Matthias Clasen's avatar
Matthias Clasen committed
146
 *
Carlos Garnacho's avatar
Carlos Garnacho committed
147 148
 * /&ast; Theme a label named title-label &ast;/
 * GtkLabel&num;title-label {
Matthias Clasen's avatar
Matthias Clasen committed
149
 *     font-name: Sans 15
Carlos Garnacho's avatar
Carlos Garnacho committed
150 151 152 153
 * }
 *
 * /&ast; Theme any widget named main-entry &ast;/
 * &num;main-entry {
Matthias Clasen's avatar
Matthias Clasen committed
154
 *     background-color: &num;f0a810
Carlos Garnacho's avatar
Carlos Garnacho committed
155 156 157 158
 * }
 * </programlisting>
 * </example>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
159 160 161 162 163 164
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
165 166
 * style classes they define and see <xref linkend="gtkstylecontext-classes"/>
 * for a list of all style classes used by GTK+ widgets.
Carlos Garnacho's avatar
Carlos Garnacho committed
167
 * </para>
168 169 170 171 172 173
 * <para>
 * Note that there is some ambiguity in the selector syntax when it comes
 * to differentiation widget class names from regions. GTK+ currently treats
 * a string as a widget class name if it contains any uppercase characters
 * (which should work for more widgets with names like GtkLabel).
 * </para>
Carlos Garnacho's avatar
Carlos Garnacho committed
174
 * <example>
Matthias Clasen's avatar
Matthias Clasen committed
175 176
 * <title>Style classes in selectors</title>
 * <programlisting language="text">
Carlos Garnacho's avatar
Carlos Garnacho committed
177 178
 * /&ast; Theme all widgets defining the class entry &ast;/
 * .entry {
Carlos Garnacho's avatar
Carlos Garnacho committed
179
 *     color: &num;39f1f9;
Carlos Garnacho's avatar
Carlos Garnacho committed
180 181 182 183
 * }
 *
 * /&ast; Theme spinbuttons' entry &ast;/
 * GtkSpinButton.entry {
Matthias Clasen's avatar
Matthias Clasen committed
184
 *     color: &num;900185
Carlos Garnacho's avatar
Carlos Garnacho committed
185 186 187 188
 * }
 * </programlisting>
 * </example>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
 * 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
204 205 206
 * regions and pseudo-classes they define and see
 * <xref linkend="gtkstylecontext-classes"/> for a list of all regions
 * used by GTK+ widgets.
Carlos Garnacho's avatar
Carlos Garnacho committed
207 208
 * </para>
 * <example>
Matthias Clasen's avatar
Matthias Clasen committed
209 210
 * <title>Regions in selectors</title>
 * <programlisting language="text">
Carlos Garnacho's avatar
Carlos Garnacho committed
211 212
 * /&ast; Theme any label within a notebook &ast;/
 * GtkNotebook GtkLabel {
Carlos Garnacho's avatar
Carlos Garnacho committed
213
 *     color: &num;f90192;
Carlos Garnacho's avatar
Carlos Garnacho committed
214 215 216
 * }
 *
 * /&ast; Theme labels within notebook tabs &ast;/
Matthias Clasen's avatar
Matthias Clasen committed
217
 * GtkNotebook tab GtkLabel {
Carlos Garnacho's avatar
Carlos Garnacho committed
218
 *     color: &num;703910;
Carlos Garnacho's avatar
Carlos Garnacho committed
219 220 221 222 223 224
 * }
 *
 * /&ast; Theme labels in the any first notebook
 *  tab, both selectors are equivalent &ast;/
 * GtkNotebook tab:nth-child(first) GtkLabel,
 * GtkNotebook tab:first-child GtkLabel {
Carlos Garnacho's avatar
Carlos Garnacho committed
225
 *     color: &num;89d012;
Carlos Garnacho's avatar
Carlos Garnacho committed
226 227 228 229
 * }
 * </programlisting>
 * </example>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
230 231 232 233 234
 * 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.
Carlos Garnacho's avatar
Carlos Garnacho committed
235 236 237
 * </para>
 * <example>
 * <title>Styling specific widget states</title>
Matthias Clasen's avatar
Matthias Clasen committed
238
 * <programlisting language="text">
Carlos Garnacho's avatar
Carlos Garnacho committed
239 240 241 242 243
 * /&ast; Theme active (pressed) buttons &ast;/
 * GtkButton:active {
 *     background-color: &num;0274d9;
 * }
 *
Matthias Clasen's avatar
Matthias Clasen committed
244 245
 * /&ast; Theme buttons with the mouse pointer on it,
 *    both are equivalent &ast;/
Carlos Garnacho's avatar
Carlos Garnacho committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
 * GtkButton:hover,
 * GtkButton:prelight {
 *     background-color: &num;3085a9;
 * }
 *
 * /&ast; Theme insensitive widgets, both are equivalent &ast;/
 * :insensitive,
 * *:insensitive {
 *     background-color: &num;320a91;
 * }
 *
 * /&ast; Theme selection colors in entries &ast;/
 * GtkEntry:selected {
 *     background-color: &num;56f9a0;
 * }
 *
 * /&ast; Theme focused labels &ast;/
 * GtkLabel:focused {
 *     background-color: &num;b4940f;
 * }
 *
 * /&ast; Theme inconsistent checkbuttons &ast;/
 * GtkCheckButton:inconsistent {
 *     background-color: &num;20395a;
 * }
 * </programlisting>
 * </example>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
274 275
 * Widget state pseudoclasses may only apply to the last element
 * in a selector.
Carlos Garnacho's avatar
Carlos Garnacho committed
276 277
 * </para>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
278 279 280
 * 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
Carlos Garnacho's avatar
Carlos Garnacho committed
281 282 283 284
 * over the others.
 * </para>
 * </refsect2>
 * <refsect2 id="gtkcssprovider-rules">
Matthias Clasen's avatar
Matthias Clasen committed
285
 * <title>&commat; Rules</title>
Carlos Garnacho's avatar
Carlos Garnacho committed
286
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
287 288
 * GTK+'s CSS supports the &commat;import rule, in order to load another
 * CSS style sheet in addition to the currently parsed one.
Carlos Garnacho's avatar
Carlos Garnacho committed
289 290 291
 * </para>
 * <example>
 * <title>Using the &commat;import rule</title>
Matthias Clasen's avatar
Matthias Clasen committed
292
 * <programlisting language="text">
Matthias Clasen's avatar
Matthias Clasen committed
293
 * &commat;import url ("path/to/common.css");
Carlos Garnacho's avatar
Carlos Garnacho committed
294 295
 * </programlisting>
 * </example>
Matthias Clasen's avatar
Matthias Clasen committed
296
 * <para id="css-binding-set">
297 298
 * In order to extend key bindings affecting different widgets, GTK+
 * supports the &commat;binding-set rule to parse a set of bind/unbind
Matthias Clasen's avatar
Matthias Clasen committed
299 300 301 302 303 304 305 306 307
 * directives, see #GtkBindingSet for the supported syntax. Note that
 * the binding sets defined in this way must be associated with rule sets
 * by setting the gtk-key-bindings style property.
 * </para>
 * <para>
 * Customized key bindings are typically defined in a separate
 * <filename>gtk-keys.css</filename> CSS file and GTK+ loads this file
 * according to the current key theme, which is defined by the
 * #GtkSettings:gtk-key-theme-name setting.
308 309 310 311 312 313 314 315 316
 * </para>
 * <example>
 * <title>Using the &commat;binding rule</title>
 * <programlisting language="text">
 * &commat;binding-set binding-set1 {
 *   bind "&lt;alt&gt;Left" { "move-cursor" (visual-positions, -3, 0) };
 *   unbind "End";
 * };
 *
317
 * &commat;binding-set binding-set2 {
318
 *   bind "&lt;alt&gt;Right" { "move-cursor" (visual-positions, 3, 0) };
319
 *   bind "&lt;alt&gt;KP_space" { "delete-from-cursor" (whitespace, 1)
Matthias Clasen's avatar
Matthias Clasen committed
320
 *                          "insert-at-cursor" (" ") };
321 322 323
 * };
 *
 * GtkEntry {
324
 *   gtk-key-bindings: binding-set1, binding-set2;
325 326 327 328
 * }
 * </programlisting>
 * </example>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
329 330 331 332
 * 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.
Carlos Garnacho's avatar
Carlos Garnacho committed
333 334 335
 * </para>
 * <example>
 * <title>Defining colors</title>
Matthias Clasen's avatar
Matthias Clasen committed
336
 * <programlisting language="text">
Carlos Garnacho's avatar
Carlos Garnacho committed
337 338 339 340 341 342 343 344 345 346 347
 * &commat;define-color bg_color &num;f9a039;
 *
 * &ast; {
 *     background-color: &commat;bg_color;
 * }
 * </programlisting>
 * </example>
 * </refsect2>
 * <refsect2 id="gtkcssprovider-symbolic-colors">
 * <title>Symbolic colors</title>
 * <para>
Matthias Clasen's avatar
Matthias Clasen committed
348 349 350 351
 * 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.
Carlos Garnacho's avatar
Carlos Garnacho committed
352 353 354
 * </para>
 * <example>
 * <title>Using symbolic colors</title>
Matthias Clasen's avatar
Matthias Clasen committed
355
 * <programlisting language="text">
Carlos Garnacho's avatar
Carlos Garnacho committed
356 357 358 359 360 361 362 363 364 365 366 367 368
 * &commat;define-color entry-color shade (&commat;bg_color, 0.7);
 *
 * GtkEntry {
 *     background-color: @entry-color;
 * }
 *
 * GtkEntry:focused {
 *     background-color: mix (&commat;entry-color,
 *                            shade (&num;fff, 0.5),
 *                            0.8);
 * }
 * </programlisting>
 * </example>
Matthias Clasen's avatar
Matthias Clasen committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
 * <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>
Matthias Clasen's avatar
Matthias Clasen committed
384 385 386 387
 *         <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>
Matthias Clasen's avatar
Matthias Clasen committed
388 389 390
 *       </row>
 *       <row>
 *         <entry>rgba(@r, @g, @b, @a)</entry>
Matthias Clasen's avatar
Matthias Clasen committed
391 392 393
 *         <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>
Matthias Clasen's avatar
Matthias Clasen committed
394 395 396
 *       </row>
 *       <row>
 *         <entry>&num;@xxyyzz</entry>
Matthias Clasen's avatar
Matthias Clasen committed
397 398 399 400 401
 *         <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>
Matthias Clasen's avatar
Matthias Clasen committed
402 403 404
 *       </row>
 *       <row>
 *         <entry>&commat;name</entry>
Matthias Clasen's avatar
Matthias Clasen committed
405 406 407
 *         <entry>Reference to a color that has been defined with
 *                &commat;define-color
 *         </entry>
Matthias Clasen's avatar
Matthias Clasen committed
408 409 410 411
 *         <entry>&commat;bg_color</entry>
 *       </row>
 *       <row>
 *         <entry>mix(@color1, @color2, @f)</entry>
Matthias Clasen's avatar
Matthias Clasen committed
412 413 414
 *         <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>
Matthias Clasen's avatar
Matthias Clasen committed
415 416 417
 *       </row>
 *       <row>
 *         <entry>shade(@color, @f)</entry>
Matthias Clasen's avatar
Matthias Clasen committed
418 419 420
 *         <entry>A lighter or darker variant of @color. @f is a
 *                floating point number.
 *         </entry>
Matthias Clasen's avatar
Matthias Clasen committed
421 422 423 424 425 426 427 428 429 430 431 432 433
 *         <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>
Carlos Garnacho's avatar
Carlos Garnacho committed
434
 * </refsect2>
Matthias Clasen's avatar
Matthias Clasen committed
435 436 437 438 439 440
 * <refsect2 id="gtkcssprovider-gradients">
 * <title>Gradients</title>
 * <para>
 * Linear or radial Gradients can be used as background images.
 * </para>
 * <para>
441 442
 * A linear gradient along the line from (@start_x, @start_y) to
 * (@end_x, @end_y) is specified using the syntax
Matthias Clasen's avatar
Matthias Clasen committed
443
 * <literallayout>-gtk-gradient (linear,
444
 *               @start_x @start_y, @end_x @end_y,
Matthias Clasen's avatar
Matthias Clasen committed
445 446
 *               color-stop (@position, @color),
 *               ...)</literallayout>
447 448 449
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
 * 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>
475 476
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
477 478
 * syntax
 * <literallayout>-gtk-gradient (radial,
479 480
 *                @start_x @start_y, @start_radius,
 *                @end_x @end_y, @end_radius,
Matthias Clasen's avatar
Matthias Clasen committed
481 482
 *                color-stop (@position, @color),
 *                ...)</literallayout>
483
 * where @start_radius and @end_radius are floating point numbers and
Matthias Clasen's avatar
Matthias Clasen committed
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
 * 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
499
 * <literallayout>-gtk-gradient (radial,
Matthias Clasen's avatar
Matthias Clasen committed
500 501 502 503 504 505 506 507
 *                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>
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
 * <refsect2 id="gtkcssprovider-shadows">
 * <title>Text shadow</title>
 * <para>
 * A shadow list can be applied to text or symbolic icons, using the CSS3
 * text-shadow syntax, as defined in
 * <ulink url="http://www.w3.org/TR/css3-text/#text-shadow">the CSS3 specification</ulink>.
 * </para>
 * <para>
 * A text shadow is specified using the syntax
 * <literallayout>text-shadow: @horizontal_offset @vertical_offset [ @blur_radius ] @color</literallayout>
 * The offset of the shadow is specified with the @horizontal_offset and @vertical_offset
 * parameters. The optional blur radius is parsed, but it is currently not rendered by
 * the GTK+ theming engine.
 * </para>
 * <para>
 * To set multiple shadows on an element, you can specify a comma-separated list
 * of shadow elements in the text-shadow property. Shadows are always rendered
 * front-back, i.e. the first shadow specified is on top of the others. Shadows
 * can thus overlay each other, but they can never overlay the text itself,
 * which is always rendered on top of the shadow layer.
 * </para>
 * </refsect2>
530
 * <refsect2 id="gtkcssprovider-slices">
Matthias Clasen's avatar
Matthias Clasen committed
531
 * <title>Border images</title>
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
 * <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"/>
551 552 553
 * <para>This border image was specified with
 * <literallayout>url("gradient1.png") 10 10 10 10</literallayout>
 * </para>
554 555 556 557
 * </example>
 * <example>
 * <title>A repeating border image</title>
 * <inlinegraphic fileref="border2.png" format="PNG"/>
558 559 560
 * <para>This border image was specified with
 * <literallayout>url("gradient1.png") 10 10 10 10 repeat</literallayout>
 * </para>
561 562 563 564
 * </example>
 * <example>
 * <title>A stretched border image</title>
 * <inlinegraphic fileref="border3.png" format="PNG"/>
565 566 567
 * <para>This border image was specified with
 * <literallayout>url("gradient1.png") 10 10 10 10 stretch</literallayout>
 * </para>
568 569
 * </example>
 * </refsect2>
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
 * <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>
Carlos Garnacho's avatar
Carlos Garnacho committed
597 598 599 600 601 602 603 604 605 606 607 608 609
 * <refsect2 id="gtkcssprovider-properties">
 * <title>Supported properties</title>
 * <para>
 * Properties are the part that differ the most to common CSS,
 * not all properties are supported (some are planned to be
 * supported eventually, some others are meaningless or don't
 * map intuitively in a widget based environment).
 * </para>
 * <para>
 * There is also a difference in shorthand properties, for
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
610
 * @font property is supported.
Carlos Garnacho's avatar
Carlos Garnacho committed
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
 * </para>
 * <para>
 * The currently supported properties are:
 * </para>
 * <informaltable>
 *   <tgroup cols="4">
 *     <thead>
 *       <row>
 *         <entry>Property name</entry>
 *         <entry>Syntax</entry>
 *         <entry>Maps to</entry>
 *         <entry>Examples</entry>
 *       </row>
 *     </thead>
 *     <tbody>
 *       <row>
 *         <entry>engine</entry>
Matthias Clasen's avatar
Matthias Clasen committed
628
 *         <entry>engine-name</entry>
Carlos Garnacho's avatar
Carlos Garnacho committed
629
 *         <entry>#GtkThemingEngine</entry>
630 631
 *         <entry>engine: clearlooks;
 *  engine: none; /&ast; use the default (i.e. builtin) engine) &ast;/ </entry>
Carlos Garnacho's avatar
Carlos Garnacho committed
632 633 634
 *       </row>
 *       <row>
 *         <entry>background-color</entry>
Matthias Clasen's avatar
Matthias Clasen committed
635 636 637
 *         <entry morerows="2">color (see above)</entry>
 *         <entry morerows="2">#GdkRGBA</entry>
 *         <entry morerows="2"><literallayout>background-color: &num;fff;
638 639 640
 * color: &amp;color1;
 * background-color: shade (&amp;color1, 0.5);
 * color: mix (&amp;color1, &num;f0f, 0.8);</literallayout>
Carlos Garnacho's avatar
Carlos Garnacho committed
641 642 643
 *         </entry>
 *       </row>
 *       <row>
Carlos Garnacho's avatar
Carlos Garnacho committed
644
 *         <entry>color</entry>
Carlos Garnacho's avatar
Carlos Garnacho committed
645 646
 *       </row>
 *       <row>
Matthias Clasen's avatar
Matthias Clasen committed
647 648 649
 *         <entry>border-color</entry>
 *       </row>
 *       <row>
Carlos Garnacho's avatar
Carlos Garnacho committed
650
 *         <entry>font</entry>
651
 *         <entry>@family [@style] [@size]</entry>
Carlos Garnacho's avatar
Carlos Garnacho committed
652
 *         <entry>#PangoFontDescription</entry>
Matthias Clasen's avatar
Matthias Clasen committed
653
 *         <entry>font: Sans 15;</entry>
Carlos Garnacho's avatar
Carlos Garnacho committed
654 655 656
 *       </row>
 *       <row>
 *         <entry>margin</entry>
657
 *         <entry morerows="1"><literallayout>@width
658 659 660
 * @vertical_width @horizontal_width
 * @top_width @horizontal_width @bottom_width
 * @top_width @right_width @bottom_width @left_width</literallayout>
Carlos Garnacho's avatar
Carlos Garnacho committed
661 662
 *         </entry>
 *         <entry morerows="1">#GtkBorder</entry>
Matthias Clasen's avatar
Matthias Clasen committed
663
 *         <entry morerows="1"><literallayout>margin: 5;
Carlos Garnacho's avatar
Carlos Garnacho committed
664 665
 * margin: 5 10;
 * margin: 5 10 3;
Matthias Clasen's avatar
Matthias Clasen committed
666
 * margin: 5 10 3 5;</literallayout>
Carlos Garnacho's avatar
Carlos Garnacho committed
667 668 669 670 671 672 673
 *         </entry>
 *       </row>
 *       <row>
 *         <entry>padding</entry>
 *       </row>
 *       <row>
 *         <entry>background-image</entry>
674 675
 *         <entry><literallayout>gradient (see above) or
 * url(@path)</literallayout></entry>
Carlos Garnacho's avatar
Carlos Garnacho committed
676
 *         <entry>#cairo_pattern_t</entry>
Matthias Clasen's avatar
Matthias Clasen committed
677
 *         <entry><literallayout>-gtk-gradient (linear,
678
 *                left top, right top,
Carlos Garnacho's avatar
Carlos Garnacho committed
679 680 681 682 683 684 685 686 687
 *                from (&num;fff), to (&num;000));
 * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
 *                from (&num;fff),
 *                color-stop (0.5, &num;f00),
 *                to (&num;000));
 * -gtk-gradient (radial,
 *                center center, 0.2,
 *                center center, 0.8,
 *                color-stop (0.0, &num;fff),
688 689
 *                color-stop (1.0, &num;000));
 * url ('background.png');</literallayout>
Carlos Garnacho's avatar
Carlos Garnacho committed
690 691 692
 *         </entry>
 *       </row>
 *       <row>
Matthias Clasen's avatar
Matthias Clasen committed
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
 *         <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>
Carlos Garnacho's avatar
Carlos Garnacho committed
711
 *         <entry>border-image</entry>
712
 *         <entry><literallayout>border image (see above)</literallayout></entry>
713
 *         <entry>internal use only</entry>
Matthias Clasen's avatar
Matthias Clasen committed
714 715
 *         <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>
Carlos Garnacho's avatar
Carlos Garnacho committed
716 717
 *         </entry>
 *       </row>
Benjamin Otte's avatar
Benjamin Otte committed
718
 *       <row>
719 720 721 722 723
 *         <entry>text-shadow</entry>
 *         <entry>shadow list (see above)</entry>
 *         <entry>#GtkTextShadow</entry>
 *         <entry><literallayout>text-shadow: 1 1 0 blue, -4 -4 red;</literallayout></entry>
 *       </row>
Carlos Garnacho's avatar
Carlos Garnacho committed
724 725
 *       <row>
 *         <entry>transition</entry>
726
 *         <entry>transition (see above)</entry>
727
 *         <entry>internal use only</entry>
Matthias Clasen's avatar
Matthias Clasen committed
728 729
 *         <entry><literallayout>transition: 150ms ease-in-out;
 * transition: 1s linear loop;</literallayout>
Carlos Garnacho's avatar
Carlos Garnacho committed
730 731
 *         </entry>
 *       </row>
732 733 734 735 736 737 738
 *       <row>
 *         <entry>gtk-key-bindings</entry>
 *         <entry>binding set name list</entry>
 *         <entry>internal use only</entry>
 *         <entry><literallayout>gtk-bindings: binding1, binding2, ...;</literallayout>
 *         </entry>
 *       </row>
Carlos Garnacho's avatar
Carlos Garnacho committed
739 740 741
 *     </tbody>
 *   </tgroup>
 * </informaltable>
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
 * <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>
Carlos Garnacho's avatar
Carlos Garnacho committed
763 764 765
 * </refsect2>
 */

766
typedef struct GtkCssRuleset GtkCssRuleset;
Benjamin Otte's avatar
Benjamin Otte committed
767
typedef struct _GtkCssScanner GtkCssScanner;
Carlos Garnacho's avatar
Carlos Garnacho committed
768
typedef enum ParserScope ParserScope;
769
typedef enum ParserSymbol ParserSymbol;
Carlos Garnacho's avatar
Carlos Garnacho committed
770

771
struct GtkCssRuleset
Carlos Garnacho's avatar
Carlos Garnacho committed
772
{
Benjamin Otte's avatar
Benjamin Otte committed
773
  GtkCssSelector *selector;
774
  GHashTable *widget_style;
Carlos Garnacho's avatar
Carlos Garnacho committed
775
  GHashTable *style;
776 777

  guint has_inherit :1;
Carlos Garnacho's avatar
Carlos Garnacho committed
778 779
};

Benjamin Otte's avatar
Benjamin Otte committed
780
struct _GtkCssScanner
781
{
Benjamin Otte's avatar
Benjamin Otte committed
782 783 784
  GtkCssProvider *provider;
  GtkCssParser *parser;
  GtkCssScanner *parent;
785
  GFile *file;
786
  GFile *base;
787
  GSList *state;
788 789
  GSList *cur_selectors;
  GHashTable *cur_properties;
790 791
};

792
struct _GtkCssProviderPrivate
Carlos Garnacho's avatar
Carlos Garnacho committed
793 794
{
  GScanner *scanner;
795

796 797
  GHashTable *symbolic_colors;

798
  GArray *rulesets;
Carlos Garnacho's avatar
Carlos Garnacho committed
799 800 801 802
};

enum ParserScope {
  SCOPE_SELECTOR,
803
  SCOPE_PSEUDO_CLASS,
804
  SCOPE_NTH_CHILD,
Carlos Garnacho's avatar
Carlos Garnacho committed
805
  SCOPE_DECLARATION,
Matthias Clasen's avatar
Matthias Clasen committed
806 807
  SCOPE_VALUE,
  SCOPE_BINDING_SET
Carlos Garnacho's avatar
Carlos Garnacho committed
808 809
};

810 811 812 813 814
/* Extend GtkStateType, since these
 * values are also used as symbols
 */
enum ParserSymbol {
  /* Scope: pseudo-class */
Carlos Garnacho's avatar
Carlos Garnacho committed
815
  SYMBOL_NTH_CHILD = GTK_STATE_FOCUSED + 1,
816 817
  SYMBOL_FIRST_CHILD,
  SYMBOL_LAST_CHILD,
818
  SYMBOL_SORTED_CHILD,
819 820 821 822 823 824 825 826

  /* Scope: nth-child */
  SYMBOL_NTH_CHILD_EVEN,
  SYMBOL_NTH_CHILD_ODD,
  SYMBOL_NTH_CHILD_FIRST,
  SYMBOL_NTH_CHILD_LAST
};

827 828 829 830 831 832 833
enum {
  PARSING_ERROR,
  LAST_SIGNAL
};

static guint css_provider_signals[LAST_SIGNAL] = { 0 };

Carlos Garnacho's avatar
Carlos Garnacho committed
834 835 836
static void gtk_css_provider_finalize (GObject *object);
static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);

837 838
static gboolean
gtk_css_provider_load_internal (GtkCssProvider *css_provider,
Benjamin Otte's avatar
Benjamin Otte committed
839
                                GtkCssScanner  *scanner,
840 841 842 843
                                GFile          *file,
                                const char     *data,
                                gsize           length,
                                GError        **error);
844

Matthias Clasen's avatar
Matthias Clasen committed
845 846 847 848 849 850
GQuark
gtk_css_provider_error_quark (void)
{
  return g_quark_from_static_string ("gtk-css-provider-error-quark");
}

Carlos Garnacho's avatar
Carlos Garnacho committed
851 852 853 854
G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
                                               gtk_css_style_provider_iface_init));

855 856 857 858 859 860 861
static void
gtk_css_provider_parsing_error (GtkCssProvider  *provider,
                                const gchar     *path,
                                guint            line,
                                guint            position,
                                const GError *   error)
{
862 863 864 865 866 867 868 869 870 871 872 873 874
  /* Only emit a warning when we have no error handlers. This is our
   * default handlers. And in this case erroneous CSS files are a bug
   * and should be fixed.
   * Note that these warnings can also be triggered by a broken theme
   * that people installed from some weird location on the internets.
   */
  if (!g_signal_has_handler_pending (provider,
                                     css_provider_signals[PARSING_ERROR],
                                     0,
                                     TRUE))
    {
      g_warning ("Theme parsing error: %s:%u:%u: %s", path ? path : "<unknown>", line, position, error->message);
    }
875 876
}

Carlos Garnacho's avatar
Carlos Garnacho committed
877 878 879 880 881
static void
gtk_css_provider_class_init (GtkCssProviderClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
  /**
   * GtkCssProvider::parsing-error:
   * @provider: the provider that had a parsing error
   * @path: path to the parsed file or %NULL if the file cannot be
   *   identified or the data was not loaded from a file
   * @line: line in the file or data or 0 if unknown
   * @position: offset into the current line or 0 if unknown or the
   *   whole line is affected
   * @error: The parsing error
   *
   * Signals that a parsing error occured. the @path, @line and @position
   * describe the actual location of the error as accurately as possible.
   *
   * Parsing errors are never fatal, so the parsing will resume after
   * the error. Errors may however cause parts of the given
   * data or even all of it to not be parsed at all. So it is a useful idea
   * to check that the parsing succeeds by connecting to this signal.
   *
   * Note that this signal may be emitted at any time as the css provider
   * may opt to defer parsing parts or all of the input to a later time
   * than when a loading function was called.
   */
  css_provider_signals[PARSING_ERROR] =
    g_signal_new (I_("parsing-error"),
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkCssProviderClass, parsing_error),
                  NULL, NULL,
                  _gtk_marshal_VOID__STRING_UINT_UINT_BOXED,
                  G_TYPE_NONE, 4,
                  G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_ERROR);

Carlos Garnacho's avatar
Carlos Garnacho committed
914 915
  object_class->finalize = gtk_css_provider_finalize;

916 917
  klass->parsing_error = gtk_css_provider_parsing_error;

Carlos Garnacho's avatar
Carlos Garnacho committed
918 919 920
  g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
}

921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
static void
gtk_css_provider_take_error_full (GtkCssProvider *provider,
                                  GFile          *file,
                                  guint           line,
                                  guint           position,
                                  GError         *error)
{
  char *filename;

  if (file)
    filename = g_file_get_path (file);
  else
    filename = NULL;

  g_signal_emit (provider, css_provider_signals[PARSING_ERROR], 0,
                 filename, line, position, error);

  g_free (filename);
  g_error_free (error);
}

942 943 944 945
static void
gtk_css_ruleset_init_copy (GtkCssRuleset       *new,
                           const GtkCssRuleset *ruleset,
                           GtkCssSelector      *selector)
Carlos Garnacho's avatar
Carlos Garnacho committed
946
{
947
  memcpy (new, ruleset, sizeof (GtkCssRuleset));
Carlos Garnacho's avatar
Carlos Garnacho committed
948

949
  new->selector = selector;
950 951
  if (new->widget_style)
    g_hash_table_ref (new->widget_style);
952 953
  if (new->style)
    g_hash_table_ref (new->style);
Carlos Garnacho's avatar
Carlos Garnacho committed
954 955 956
}

static void
957
gtk_css_ruleset_clear (GtkCssRuleset *ruleset)
Carlos Garnacho's avatar
Carlos Garnacho committed
958
{
959 960
  if (ruleset->style)
    g_hash_table_unref (ruleset->style);
961 962
  if (ruleset->widget_style)
    g_hash_table_unref (ruleset->widget_style);
963 964
  if (ruleset->selector)
    _gtk_css_selector_free (ruleset->selector);
965

966
  memset (ruleset, 0, sizeof (GtkCssRuleset));
Carlos Garnacho's avatar
Carlos Garnacho committed
967 968 969
}

static void
970
property_value_free (GValue *value)
Carlos Garnacho's avatar
Carlos Garnacho committed
971
{
972 973
  if (G_IS_VALUE (value))
    g_value_unset (value);
Carlos Garnacho's avatar
Carlos Garnacho committed
974

975 976 977 978 979 980 981 982
  g_slice_free (GValue, value);
}

static void
gtk_css_ruleset_add_style (GtkCssRuleset *ruleset,
                           char          *name,
                           GValue        *value)
{
983 984 985 986 987
  if (ruleset->widget_style == NULL)
    ruleset->widget_style = g_hash_table_new_full (g_str_hash,
                                                   g_str_equal,
                                                   (GDestroyNotify) g_free,
                                                   (GDestroyNotify) property_value_free);
988

989
  g_hash_table_insert (ruleset->widget_style, name, value);
Carlos Garnacho's avatar
Carlos Garnacho committed
990 991
}

992 993 994 995 996
static void
gtk_css_ruleset_add (GtkCssRuleset *ruleset,
                     GParamSpec    *pspec,
                     GValue        *value)
{
997 998 999 1000 1001 1002
  if (ruleset->style == NULL)
    ruleset->style = g_hash_table_new_full (g_direct_hash,
                                            g_direct_equal,
                                            NULL,
                                            (GDestroyNotify) property_value_free);

1003
  ruleset->has_inherit |= gtk_style_param_get_inherit (pspec);
1004
  g_hash_table_insert (ruleset->style, pspec, value);
1005 1006
}

1007 1008 1009 1010 1011 1012 1013 1014
static gboolean
gtk_css_ruleset_matches (GtkCssRuleset *ruleset,
                         GtkWidgetPath *path,
                         guint          length)
{
  return _gtk_css_selector_matches (ruleset->selector, path, length);
}

1015
static void
Benjamin Otte's avatar
Benjamin Otte committed
1016
gtk_css_scanner_reset (GtkCssScanner *scanner)
1017
{
Benjamin Otte's avatar
Benjamin Otte committed
1018 1019
  g_slist_free (scanner->state);
  scanner->state = NULL;
1020

Benjamin Otte's avatar
Benjamin Otte committed
1021
  g_slist_free_full (scanner->cur_selectors, (GDestroyNotify) _gtk_css_selector_free);
Benjamin Otte's avatar
Benjamin Otte committed
1022
  scanner->cur_selectors = NULL;
1023

Benjamin Otte's avatar
Benjamin Otte committed
1024 1025
  if (scanner->cur_properties)
    g_hash_table_unref (scanner->cur_properties);
1026

Benjamin Otte's avatar
Benjamin Otte committed
1027 1028 1029 1030 1031
  scanner ->cur_properties = g_hash_table_new_full (g_str_hash,
                                                    g_str_equal,
                                                    (GDestroyNotify) g_free,
                                                    (GDestroyNotify) property_value_free);
}
1032

Benjamin Otte's avatar
Benjamin Otte committed
1033 1034 1035 1036
static void
gtk_css_scanner_destroy (GtkCssScanner *scanner)
{
  gtk_css_scanner_reset (scanner);
1037

Benjamin Otte's avatar
Benjamin Otte committed
1038 1039 1040 1041 1042 1043 1044 1045
  g_object_unref (scanner->provider);
  if (scanner->file)
    g_object_unref (scanner->file);
  g_object_unref (scanner->base);
  g_hash_table_destroy (scanner->cur_properties);
  _gtk_css_parser_free (scanner->parser);

  g_slice_free (GtkCssScanner, scanner);
1046 1047
}

1048
static void
Benjamin Otte's avatar
Benjamin Otte committed
1049 1050 1051
gtk_css_scanner_parser_error (GtkCssParser *parser,
                              const GError *error,
                              gpointer      user_data)
1052
{
Benjamin Otte's avatar
Benjamin Otte committed
1053
  GtkCssScanner *scanner = user_data;
1054

Benjamin Otte's avatar
Benjamin Otte committed
1055 1056 1057 1058 1059
  gtk_css_provider_take_error_full (scanner->provider,
                                    scanner->file,
                                    _gtk_css_parser_get_line (scanner->parser),
                                    _gtk_css_parser_get_position (scanner->parser),
                                    g_error_copy (error));
1060 1061
}

Benjamin Otte's avatar
Benjamin Otte committed
1062 1063 1064 1065 1066 1067
static GtkCssScanner *
gtk_css_scanner_new (GtkCssProvider *provider,
                     GtkCssScanner  *parent,
                     GFile          *file,
                     const gchar    *data,
                     gsize           length)
Carlos Garnacho's avatar
Carlos Garnacho committed
1068
{
Benjamin Otte's avatar
Benjamin Otte committed
1069
  GtkCssScanner *scanner;
Carlos Garnacho's avatar
Carlos Garnacho committed
1070

Benjamin Otte's avatar
Benjamin Otte committed
1071
  g_assert (data[length] == 0);
Carlos Garnacho's avatar
Carlos Garnacho committed
1072

Benjamin Otte's avatar
Benjamin Otte committed
1073
  scanner = g_slice_new0 (GtkCssScanner);
1074

Benjamin Otte's avatar
Benjamin Otte committed
1075 1076 1077
  g_object_ref (provider);
  scanner->provider = provider;
  scanner->parent = parent;
1078

1079
  if (file)
1080
    {
Benjamin Otte's avatar
Benjamin Otte committed
1081 1082
      scanner->file = g_object_ref (file);
      scanner->base = g_file_get_parent (file);
1083 1084 1085 1086
    }
  else
    {
      char *dir = g_get_current_dir ();
Benjamin Otte's avatar
Benjamin Otte committed
1087
      scanner->base = g_file_new_for_path (dir);
1088 1089
      g_free (dir);
    }
1090

Benjamin Otte's avatar
Benjamin Otte committed
1091 1092 1093 1094
  scanner->cur_properties = g_hash_table_new_full (g_str_hash,
                                                   g_str_equal,
                                                   (GDestroyNotify) g_free,
                                                   (GDestroyNotify) property_value_free);
1095

Benjamin Otte's avatar
Benjamin Otte committed
1096 1097 1098
  scanner->parser = _gtk_css_parser_new (data,
                                         gtk_css_scanner_parser_error,
                                         scanner);
1099

1100 1101 1102
  return scanner;
}

1103
static GFile *
Benjamin Otte's avatar
Benjamin Otte committed
1104
gtk_css_scanner_get_base_url (GtkCssScanner *scanner)
1105
{
Benjamin Otte's avatar
Benjamin Otte committed
1106
  return scanner->base;
1107 1108
}

1109
static gboolean
Benjamin Otte's avatar
Benjamin Otte committed
1110 1111
gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
                               GFile         *file)
1112 1113 1114
{
  while (scanner)
    {
Benjamin Otte's avatar
Benjamin Otte committed
1115
      if (scanner->file && g_file_equal (scanner->file, file))
1116 1117
        return TRUE;

Benjamin Otte's avatar
Benjamin Otte committed
1118
      scanner = scanner->parent;
1119 1120 1121 1122 1123
    }

  return FALSE;
}

1124 1125 1126 1127 1128 1129 1130 1131 1132
static void
gtk_css_provider_init (GtkCssProvider *css_provider)
{
  GtkCssProviderPrivate *priv;

  priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider,
                                                           GTK_TYPE_CSS_PROVIDER,
                                                           GtkCssProviderPrivate);

1133
  priv->rulesets = g_array_new (FALSE, FALSE, sizeof (GtkCssRuleset));
1134 1135 1136 1137

  priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                 (GDestroyNotify) g_free,
                                                 (GDestroyNotify) gtk_symbolic_color_unref);
Carlos Garnacho's avatar
Carlos Garnacho committed
1138 1139
}

1140
static void
1141 1142
css_provider_dump_symbolic_colors (GtkCssProvider     *css_provider,
                                   GtkStyleProperties *props)