gtkcssprovider.c 68.7 KB
Newer Older
Carlos Garnacho's avatar
Carlos Garnacho committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Carlos Garnacho's avatar
Carlos Garnacho committed
16 17 18 19 20 21
 */

#include "config.h"

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

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

26 27
#include "gtkcssproviderprivate.h"

28
#include "gtkbitmaskprivate.h"
29
#include "gtkcssarrayvalueprivate.h"
30
#include "gtkcsscolorvalueprivate.h"
31
#include "gtkcsskeyframesprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
32
#include "gtkcssparserprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
33
#include "gtkcsssectionprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
34
#include "gtkcssselectorprivate.h"
35
#include "gtkcssshorthandpropertyprivate.h"
36
#include "gtkcssstylefuncsprivate.h"
37
#include "gtksettingsprivate.h"
38 39
#include "gtkstyleprovider.h"
#include "gtkstylecontextprivate.h"
40
#include "gtkstylepropertyprivate.h"
41
#include "gtkstyleproviderprivate.h"
42
#include "gtkwidgetpath.h"
43
#include "gtkbindings.h"
44
#include "gtkmarshalers.h"
45
#include "gtkprivate.h"
46
#include "gtkintl.h"
47
#include "gtkversion.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
48

Carlos Garnacho's avatar
Carlos Garnacho committed
49 50 51 52 53 54
/**
 * SECTION:gtkcssprovider
 * @Short_description: CSS-like styling for widgets
 * @Title: GtkCssProvider
 * @See_also: #GtkStyleContext, #GtkStyleProvider
 *
Matthias Clasen's avatar
Matthias Clasen committed
55
 * GtkCssProvider is an object implementing the #GtkStyleProvider interface.
Matthias Clasen's avatar
Matthias Clasen committed
56
 * It is able to parse [CSS-like][css-overview] input in order to style widgets.
Carlos Garnacho's avatar
Carlos Garnacho committed
57
 *
Matthias Clasen's avatar
Matthias Clasen committed
58 59 60 61 62 63 64 65 66 67
 * An application can make GTK+ parse a specific CSS style sheet by calling
 * gtk_css_provider_load_from_file() or gtk_css_provider_load_from_resouce()
 * 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 $XDG_CONFIG_HOME/gtk-3.0/gtk.css` is loaded if it exists. Then, GTK+ tries
 * to load `$HOME/.themes/theme-name/gtk-3.0/gtk.css`, falling back to
 * `DATADIR/share/themes/THEME/gtk-VERSION/gtk.css`, where THEME is the name of
 * the current theme (see the #GtkSettings:gtk-theme-name setting), DATADIR
Matthias Clasen's avatar
Matthias Clasen committed
68
 * is the prefix configured when GTK+ was compiled, unless overridden by the
Matthias Clasen's avatar
Matthias Clasen committed
69 70 71
 * `GTK_DATA_PREFIX` environment variable, and VERSION is the GTK+ version number.
 * If no file is found for the current version, GTK+ tries older versions all the
 * way back to 3.0.
72
 *
Matthias Clasen's avatar
Matthias Clasen committed
73 74
 * In the same way, GTK+ tries to load a gtk-keys.css file for the current
 * key theme, as defined by #GtkSettings:gtk-key-theme-name.
Carlos Garnacho's avatar
Carlos Garnacho committed
75 76
 */

Matthias Clasen's avatar
Matthias Clasen committed
77

78
typedef struct GtkCssRuleset GtkCssRuleset;
Benjamin Otte's avatar
Benjamin Otte committed
79
typedef struct _GtkCssScanner GtkCssScanner;
80 81
typedef struct _PropertyValue PropertyValue;
typedef struct _WidgetPropertyValue WidgetPropertyValue;
Carlos Garnacho's avatar
Carlos Garnacho committed
82
typedef enum ParserScope ParserScope;
83
typedef enum ParserSymbol ParserSymbol;
Carlos Garnacho's avatar
Carlos Garnacho committed
84

85 86
struct _PropertyValue {
  GtkCssStyleProperty *property;
87
  GtkCssValue         *value;
88 89 90 91 92
  GtkCssSection       *section;
};

struct _WidgetPropertyValue {
  WidgetPropertyValue *next;
93 94
  char *name;
  char *value;
95 96 97 98

  GtkCssSection *section;
};

99
struct GtkCssRuleset
Carlos Garnacho's avatar
Carlos Garnacho committed
100
{
Benjamin Otte's avatar
Benjamin Otte committed
101
  GtkCssSelector *selector;
102
  GtkCssSelectorTree *selector_match;
103
  WidgetPropertyValue *widget_style;
104
  PropertyValue *styles;
105
  GtkBitmask *set_styles;
106 107
  guint n_styles;
  guint owns_styles : 1;
108
  guint owns_widget_style : 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
109 110
};

Benjamin Otte's avatar
Benjamin Otte committed
111
struct _GtkCssScanner
112
{
Benjamin Otte's avatar
Benjamin Otte committed
113 114
  GtkCssProvider *provider;
  GtkCssParser *parser;
115
  GtkCssSection *section;
Benjamin Otte's avatar
Benjamin Otte committed
116
  GtkCssScanner *parent;
117
  GSList *state;
118 119
};

120
struct _GtkCssProviderPrivate
Carlos Garnacho's avatar
Carlos Garnacho committed
121 122
{
  GScanner *scanner;
123

124
  GHashTable *symbolic_colors;
125
  GHashTable *keyframes;
126

127
  GArray *rulesets;
128
  GtkCssSelectorTree *tree;
129
  GResource *resource;
Carlos Garnacho's avatar
Carlos Garnacho committed
130 131
};

132 133 134 135 136
enum {
  PARSING_ERROR,
  LAST_SIGNAL
};

137 138
static gboolean gtk_keep_css_sections = FALSE;

139 140
static guint css_provider_signals[LAST_SIGNAL] = { 0 };

Carlos Garnacho's avatar
Carlos Garnacho committed
141 142
static void gtk_css_provider_finalize (GObject *object);
static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
143
static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface);
144
static void widget_property_value_list_free (WidgetPropertyValue *head);
145 146 147
static void gtk_css_style_provider_emit_error (GtkStyleProviderPrivate *provider,
                                               GtkCssSection           *section,
                                               const GError            *error);
Carlos Garnacho's avatar
Carlos Garnacho committed
148

149 150
static gboolean
gtk_css_provider_load_internal (GtkCssProvider *css_provider,
Benjamin Otte's avatar
Benjamin Otte committed
151
                                GtkCssScanner  *scanner,
152 153 154
                                GFile          *file,
                                const char     *data,
                                GError        **error);
155

Matthias Clasen's avatar
Matthias Clasen committed
156 157 158 159 160 161
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
162
G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
163
                        G_ADD_PRIVATE (GtkCssProvider)
Carlos Garnacho's avatar
Carlos Garnacho committed
164
                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
165 166 167
                                               gtk_css_style_provider_iface_init)
                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER_PRIVATE,
                                               gtk_css_style_provider_private_iface_init));
Carlos Garnacho's avatar
Carlos Garnacho committed
168

169 170
static void
gtk_css_provider_parsing_error (GtkCssProvider  *provider,
171 172
                                GtkCssSection   *section,
                                const GError    *error)
173
{
174 175 176 177 178 179 180 181 182 183 184
  /* 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))
    {
185
      char *s = _gtk_css_section_to_string (section);
186

187 188
      g_warning ("Theme parsing error: %s: %s",
                 s,
189 190
                 error->message);

191
      g_free (s);
192
    }
193 194
}

195 196 197 198 199 200 201 202 203
/* This is exported privately for use in GtkInspector.
 * It is the callers responsibility to reparse the current theme.
 */
void
gtk_css_provider_set_keep_css_sections (void)
{
  gtk_keep_css_sections = TRUE;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
204 205 206 207 208
static void
gtk_css_provider_class_init (GtkCssProviderClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

209
  if (g_getenv ("GTK_CSS_DEBUG"))
210
    gtk_css_provider_set_keep_css_sections ();
211

212 213 214
  /**
   * GtkCssProvider::parsing-error:
   * @provider: the provider that had a parsing error
215
   * @section: section the error happened in
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
   * @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,
236 237
                  _gtk_marshal_VOID__BOXED_BOXED,
                  G_TYPE_NONE, 2, GTK_TYPE_CSS_SECTION, G_TYPE_ERROR);
238

Carlos Garnacho's avatar
Carlos Garnacho committed
239 240
  object_class->finalize = gtk_css_provider_finalize;

241
  klass->parsing_error = gtk_css_provider_parsing_error;
Carlos Garnacho's avatar
Carlos Garnacho committed
242 243
}

244 245
static void
gtk_css_ruleset_init_copy (GtkCssRuleset       *new,
246
                           GtkCssRuleset       *ruleset,
247
                           GtkCssSelector      *selector)
Carlos Garnacho's avatar
Carlos Garnacho committed
248
{
249
  memcpy (new, ruleset, sizeof (GtkCssRuleset));
Carlos Garnacho's avatar
Carlos Garnacho committed
250

251
  new->selector = selector;
252
  /* First copy takes over ownership */
253 254
  if (ruleset->owns_styles)
    ruleset->owns_styles = FALSE;
255 256
  if (ruleset->owns_widget_style)
    ruleset->owns_widget_style = FALSE;
257 258
  if (new->set_styles)
    new->set_styles = _gtk_bitmask_copy (new->set_styles);
Carlos Garnacho's avatar
Carlos Garnacho committed
259 260 261
}

static void
262
gtk_css_ruleset_clear (GtkCssRuleset *ruleset)
Carlos Garnacho's avatar
Carlos Garnacho committed
263
{
264 265 266 267 268 269
  if (ruleset->owns_styles)
    {
      guint i;

      for (i = 0; i < ruleset->n_styles; i++)
        {
270 271
          _gtk_css_value_unref (ruleset->styles[i].value);
	  ruleset->styles[i].value = NULL;
272 273
	  if (ruleset->styles[i].section)
	    gtk_css_section_unref (ruleset->styles[i].section);
274 275 276
        }
      g_free (ruleset->styles);
    }
277 278
  if (ruleset->set_styles)
    _gtk_bitmask_free (ruleset->set_styles);
279 280
  if (ruleset->owns_widget_style)
    widget_property_value_list_free (ruleset->widget_style);
281 282
  if (ruleset->selector)
    _gtk_css_selector_free (ruleset->selector);
283

284
  memset (ruleset, 0, sizeof (GtkCssRuleset));
Carlos Garnacho's avatar
Carlos Garnacho committed
285 286
}

287 288 289 290 291 292 293 294
static WidgetPropertyValue *
widget_property_value_new (char *name, GtkCssSection *section)
{
  WidgetPropertyValue *value;

  value = g_slice_new0 (WidgetPropertyValue);

  value->name = name;
295 296
  if (gtk_keep_css_sections)
    value->section = gtk_css_section_ref (section);
297 298 299 300 301 302 303

  return value;
}

static void
widget_property_value_free (WidgetPropertyValue *value)
{
304
  g_free (value->value);
305
  g_free (value->name);
306 307
  if (value->section)
    gtk_css_section_unref (value->section);
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

  g_slice_free (WidgetPropertyValue, value);
}

static void
widget_property_value_list_free (WidgetPropertyValue *head)
{
  WidgetPropertyValue *l, *next;
  for (l = head; l != NULL; l = next)
    {
      next = l->next;
      widget_property_value_free (l);
    }
}

static WidgetPropertyValue *
widget_property_value_list_remove_name (WidgetPropertyValue *head, const char *name)
{
  WidgetPropertyValue *l, **last;

  last = &head;

  for (l = head; l != NULL; l = l->next)
    {
      if (strcmp (l->name, name) == 0)
	{
	  *last = l->next;
	  widget_property_value_free (l);
	  break;
	}

      last = &l->next;
    }

  return head;
}

345 346 347
static void
gtk_css_ruleset_add_style (GtkCssRuleset *ruleset,
                           char          *name,
348
                           WidgetPropertyValue *value)
349
{
350 351 352
  value->next = widget_property_value_list_remove_name (ruleset->widget_style, name);
  ruleset->widget_style = value;
  ruleset->owns_widget_style = TRUE;
Carlos Garnacho's avatar
Carlos Garnacho committed
353 354
}

355
static void
Benjamin Otte's avatar
Benjamin Otte committed
356
gtk_css_ruleset_add (GtkCssRuleset       *ruleset,
357
                     GtkCssStyleProperty *property,
358
                     GtkCssValue         *value,
359
                     GtkCssSection       *section)
360
{
361 362 363
  guint i;

  g_return_if_fail (ruleset->owns_styles || ruleset->n_styles == 0);
Benjamin Otte's avatar
Benjamin Otte committed
364

365 366
  if (ruleset->set_styles == NULL)
    ruleset->set_styles = _gtk_bitmask_new ();
367

368 369 370
  ruleset->set_styles = _gtk_bitmask_set (ruleset->set_styles,
                                          _gtk_css_style_property_get_id (property),
                                          TRUE);
371

372 373 374 375 376 377
  ruleset->owns_styles = TRUE;

  for (i = 0; i < ruleset->n_styles; i++)
    {
      if (ruleset->styles[i].property == property)
        {
378 379
          _gtk_css_value_unref (ruleset->styles[i].value);
	  ruleset->styles[i].value = NULL;
380 381
	  if (ruleset->styles[i].section)
	    gtk_css_section_unref (ruleset->styles[i].section);
382 383 384 385 386 387 388
          break;
        }
    }
  if (i == ruleset->n_styles)
    {
      ruleset->n_styles++;
      ruleset->styles = g_realloc (ruleset->styles, ruleset->n_styles * sizeof (PropertyValue));
389
      ruleset->styles[i].value = NULL;
390 391 392
      ruleset->styles[i].property = property;
    }

393
  ruleset->styles[i].value = value;
394 395 396 397
  if (gtk_keep_css_sections)
    ruleset->styles[i].section = gtk_css_section_ref (section);
  else
    ruleset->styles[i].section = NULL;
398 399
}

Benjamin Otte's avatar
Benjamin Otte committed
400 401 402
static void
gtk_css_scanner_destroy (GtkCssScanner *scanner)
{
Benjamin Otte's avatar
Benjamin Otte committed
403 404
  if (scanner->section)
    gtk_css_section_unref (scanner->section);
Benjamin Otte's avatar
Benjamin Otte committed
405 406 407 408
  g_object_unref (scanner->provider);
  _gtk_css_parser_free (scanner->parser);

  g_slice_free (GtkCssScanner, scanner);
409 410
}

411 412 413 414 415 416 417 418
static void
gtk_css_style_provider_emit_error (GtkStyleProviderPrivate *provider,
                                   GtkCssSection           *section,
                                   const GError            *error)
{
  g_signal_emit (provider, css_provider_signals[PARSING_ERROR], 0, section, error);
}

419 420 421 422 423
static void
gtk_css_provider_emit_error (GtkCssProvider *provider,
                             GtkCssScanner  *scanner,
                             const GError   *error)
{
424 425 426
  gtk_css_style_provider_emit_error (GTK_STYLE_PROVIDER_PRIVATE (provider),
                                     scanner ? scanner->section : NULL,
                                     error);
427 428
}

429
static void
Benjamin Otte's avatar
Benjamin Otte committed
430 431 432
gtk_css_scanner_parser_error (GtkCssParser *parser,
                              const GError *error,
                              gpointer      user_data)
433
{
Benjamin Otte's avatar
Benjamin Otte committed
434
  GtkCssScanner *scanner = user_data;
435

436
  gtk_css_provider_emit_error (scanner->provider, scanner, error);
437 438
}

Benjamin Otte's avatar
Benjamin Otte committed
439 440 441
static GtkCssScanner *
gtk_css_scanner_new (GtkCssProvider *provider,
                     GtkCssScanner  *parent,
442
                     GtkCssSection  *section,
Benjamin Otte's avatar
Benjamin Otte committed
443
                     GFile          *file,
444
                     const gchar    *text)
Carlos Garnacho's avatar
Carlos Garnacho committed
445
{
Benjamin Otte's avatar
Benjamin Otte committed
446
  GtkCssScanner *scanner;
Carlos Garnacho's avatar
Carlos Garnacho committed
447

Benjamin Otte's avatar
Benjamin Otte committed
448
  scanner = g_slice_new0 (GtkCssScanner);
449

Benjamin Otte's avatar
Benjamin Otte committed
450 451 452
  g_object_ref (provider);
  scanner->provider = provider;
  scanner->parent = parent;
453 454
  if (section)
    scanner->section = gtk_css_section_ref (section);
455

456
  scanner->parser = _gtk_css_parser_new (text,
457
                                         file,
Benjamin Otte's avatar
Benjamin Otte committed
458 459
                                         gtk_css_scanner_parser_error,
                                         scanner);
460

461 462 463
  return scanner;
}

464
static gboolean
Benjamin Otte's avatar
Benjamin Otte committed
465 466
gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
                               GFile         *file)
467 468 469
{
  while (scanner)
    {
470 471
      GFile *parser_file = _gtk_css_parser_get_file (scanner->parser);
      if (parser_file && g_file_equal (parser_file, file))
472 473
        return TRUE;

Benjamin Otte's avatar
Benjamin Otte committed
474
      scanner = scanner->parent;
475 476 477 478 479
    }

  return FALSE;
}

Benjamin Otte's avatar
Benjamin Otte committed
480 481 482 483
static void
gtk_css_scanner_push_section (GtkCssScanner     *scanner,
                              GtkCssSectionType  section_type)
{
484
  GtkCssSection *section;
Benjamin Otte's avatar
Benjamin Otte committed
485

486
  section = _gtk_css_section_new (scanner->section,
Benjamin Otte's avatar
Benjamin Otte committed
487
                                  section_type,
488
                                  scanner->parser);
489

490 491
  if (scanner->section)
    gtk_css_section_unref (scanner->section);
492
  scanner->section = section;
Benjamin Otte's avatar
Benjamin Otte committed
493 494 495 496 497 498
}

static void
gtk_css_scanner_pop_section (GtkCssScanner *scanner,
                             GtkCssSectionType check_type)
{
499
  GtkCssSection *parent;
Benjamin Otte's avatar
Benjamin Otte committed
500
  
501 502 503 504 505
  g_assert (gtk_css_section_get_section_type (scanner->section) == check_type);

  parent = gtk_css_section_get_parent (scanner->section);
  if (parent)
    gtk_css_section_ref (parent);
Benjamin Otte's avatar
Benjamin Otte committed
506

507 508
  _gtk_css_section_end (scanner->section);
  gtk_css_section_unref (scanner->section);
Benjamin Otte's avatar
Benjamin Otte committed
509

510
  scanner->section = parent;
Benjamin Otte's avatar
Benjamin Otte committed
511 512
}

513 514 515 516 517
static void
gtk_css_provider_init (GtkCssProvider *css_provider)
{
  GtkCssProviderPrivate *priv;

518
  priv = css_provider->priv = gtk_css_provider_get_instance_private (css_provider);
519

520
  priv->rulesets = g_array_new (FALSE, FALSE, sizeof (GtkCssRuleset));
521 522 523

  priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                 (GDestroyNotify) g_free,
524
                                                 (GDestroyNotify) _gtk_css_value_unref);
525 526
  priv->keyframes = g_hash_table_new_full (g_str_hash, g_str_equal,
                                           (GDestroyNotify) g_free,
Matthias Clasen's avatar
Matthias Clasen committed
527
                                           (GDestroyNotify) _gtk_css_keyframes_unref);
Carlos Garnacho's avatar
Carlos Garnacho committed
528 529
}

530 531 532 533 534 535 536 537
static void
verify_tree_match_results (GtkCssProvider *provider,
			   const GtkCssMatcher *matcher,
			   GPtrArray *tree_rules)
{
#ifdef VERIFY_TREE
  GtkCssProviderPrivate *priv = provider->priv;
  GtkCssRuleset *ruleset;
538
  gboolean should_match;
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
  int i, j;

  for (i = 0; i < priv->rulesets->len; i++)
    {
      gboolean found = FALSE;

      ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);

      for (j = 0; j < tree_rules->len; j++)
	{
	  if (ruleset == tree_rules->pdata[j])
	    {
	      found = TRUE;
	      break;
	    }
	}
555 556
      should_match = _gtk_css_selector_matches (ruleset->selector, matcher);
      if (found != !!should_match)
557
	{
558
	  g_error ("expected rule '%s' to %s, but it %s",
559
		   _gtk_css_selector_to_string (ruleset->selector),
560
		   should_match ? "match" : "not match",
561 562 563 564 565 566
		   found ? "matched" : "didn't match");
	}
    }
#endif
}

567 568 569 570 571 572 573 574 575 576 577 578
static void
verify_tree_get_change_results (GtkCssProvider *provider,
				const GtkCssMatcher *matcher,
				GtkCssChange change)
{
#ifdef VERIFY_TREE
  {
    GtkCssChange verify_change = 0;
    GPtrArray *tree_rules;
    int i;

    tree_rules = _gtk_css_selector_tree_match_all (provider->priv->tree, matcher);
579
    if (tree_rules)
580
      {
581 582 583 584 585
        verify_tree_match_results (provider, matcher, tree_rules);

        for (i = tree_rules->len - 1; i >= 0; i--)
          {
	    GtkCssRuleset *ruleset;
586

587
            ruleset = tree_rules->pdata[i];
588

589 590 591 592
            verify_change |= _gtk_css_selector_get_change (ruleset->selector);
          }

        g_ptr_array_free (tree_rules, TRUE);
593 594 595 596 597 598 599
      }

    if (change != verify_change)
      {
	GString *s;

	s = g_string_new ("");
600 601 602 603
	g_string_append (s, "expected change ");
        gtk_css_change_print (verify_change, s);
        g_string_append (s, ", but it was ");
        gtk_css_change_print (change, s);
604
	if ((change & ~verify_change) != 0)
605 606 607 608
          {
	    g_string_append (s, ", unexpectedly set: ");
            gtk_css_change_print (change & ~verify_change, s);
          }
609
	if ((~change & verify_change) != 0)
610 611 612 613
          {
	    g_string_append_printf (s, ", unexpectedly not set: ");
            gtk_css_change_print (~change & verify_change, s);
          }
614 615 616 617 618 619 620 621
	g_warning (s->str);
	g_string_free (s, TRUE);
      }
  }
#endif
}


622 623 624
static gboolean
gtk_css_provider_get_style_property (GtkStyleProvider *provider,
                                     GtkWidgetPath    *path,
625
                                     GtkStateFlags     state,
626
                                     GParamSpec       *pspec,
627 628
                                     GValue           *value)
{
629 630
  GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
  GtkCssProviderPrivate *priv = css_provider->priv;
631
  WidgetPropertyValue *val;
632
  GPtrArray *tree_rules;
633
  GtkCssMatcher matcher;
634 635 636 637
  gboolean found = FALSE;
  gchar *prop_name;
  gint i;

638 639 640 641 642 643 644 645 646 647
  if (state == gtk_widget_path_iter_get_state (path, -1))
    {
      gtk_widget_path_ref (path);
    }
  else
    {
      path = gtk_widget_path_copy (path);
      gtk_widget_path_iter_set_state (path, -1, state);
    }

648
  if (!_gtk_css_matcher_init (&matcher, path, NULL))
649 650 651 652
    {
      gtk_widget_path_unref (path);
      return FALSE;
    }
653

654
  tree_rules = _gtk_css_selector_tree_match_all (priv->tree, &matcher);
655
  if (tree_rules)
656
    {
657
      verify_tree_match_results (css_provider, &matcher, tree_rules);
658

659 660 661
      prop_name = g_strdup_printf ("-%s-%s",
                                   g_type_name (pspec->owner_type),
                                   pspec->name);
662

663 664 665
      for (i = tree_rules->len - 1; i >= 0; i--)
        {
          GtkCssRuleset *ruleset = tree_rules->pdata[i];
666

667 668
          if (ruleset->widget_style == NULL)
            continue;
669

670 671 672 673 674 675 676 677 678 679 680
          for (val = ruleset->widget_style; val != NULL; val = val->next)
            {
              if (strcmp (val->name, prop_name) == 0)
                {
                  GtkCssScanner *scanner;

	          scanner = gtk_css_scanner_new (css_provider,
                                                 NULL,
                                                 val->section,
                                                 val->section != NULL ? gtk_css_section_get_file (val->section) : NULL,
                                                 val->value);
681 682
                  if (!val->section)
                    gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
683
                  found = _gtk_css_style_funcs_parse_value (value, scanner->parser);
684 685
                  if (!val->section)
                    gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
686 687 688 689 690 691 692 693
                  gtk_css_scanner_destroy (scanner);
	          break;
                }
            }

          if (found)
            break;
        }
694

695 696
      g_free (prop_name);
      g_ptr_array_free (tree_rules, TRUE);
697 698
    }

699
  gtk_widget_path_unref (path);
700 701 702 703

  return found;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
704 705 706
static void
gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
{
707
  iface->get_style_property = gtk_css_provider_get_style_property;
Carlos Garnacho's avatar
Carlos Garnacho committed
708 709
}

710
static GtkCssValue *
711 712 713 714 715 716 717 718
gtk_css_style_provider_get_color (GtkStyleProviderPrivate *provider,
                                  const char              *name)
{
  GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);

  return g_hash_table_lookup (css_provider->priv->symbolic_colors, name);
}

719 720 721 722 723 724 725 726 727
static GtkCssKeyframes *
gtk_css_style_provider_get_keyframes (GtkStyleProviderPrivate *provider,
                                      const char              *name)
{
  GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);

  return g_hash_table_lookup (css_provider->priv->keyframes, name);
}

728 729
static void
gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
730
                               const GtkCssMatcher     *matcher,
731 732
                               GtkCssLookup            *lookup,
                               GtkCssChange            *change)
733 734 735
{
  GtkCssProvider *css_provider;
  GtkCssProviderPrivate *priv;
736
  GtkCssRuleset *ruleset;
737
  guint j;
738 739
  int i;
  GPtrArray *tree_rules;
740 741 742 743

  css_provider = GTK_CSS_PROVIDER (provider);
  priv = css_provider->priv;

744
  tree_rules = _gtk_css_selector_tree_match_all (priv->tree, matcher);
745
  if (tree_rules)
746
    {
747
      verify_tree_match_results (css_provider, matcher, tree_rules);
748

749
      for (i = tree_rules->len - 1; i >= 0; i--)
750
        {
751
          ruleset = tree_rules->pdata[i];
752

753
          if (ruleset->styles == NULL)
754
            continue;
755

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
          if (!_gtk_bitmask_intersects (_gtk_css_lookup_get_missing (lookup),
                                        ruleset->set_styles))
          continue;

          for (j = 0; j < ruleset->n_styles; j++)
            {
              GtkCssStyleProperty *prop = ruleset->styles[j].property;
              guint id = _gtk_css_style_property_get_id (prop);

              if (!_gtk_css_lookup_is_missing (lookup, id))
                continue;

              _gtk_css_lookup_set (lookup,
                                   id,
                                   ruleset->styles[j].section,
                                  ruleset->styles[j].value);
            }

          if (_gtk_bitmask_is_empty (_gtk_css_lookup_get_missing (lookup)))
            break;
776
        }
777

778
      g_ptr_array_free (tree_rules, TRUE);
779
    }
780

781 782 783
  if (change)
    {
      GtkCssMatcher change_matcher;
784

785
      _gtk_css_matcher_superset_init (&change_matcher, matcher, GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS);
786

787 788 789
      *change = _gtk_css_selector_tree_get_change_all (priv->tree, &change_matcher);
      verify_tree_get_change_results (css_provider, &change_matcher, *change);
    }
790 791
}

792 793 794 795
static void
gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface)
{
  iface->get_color = gtk_css_style_provider_get_color;
796
  iface->get_keyframes = gtk_css_style_provider_get_keyframes;
797
  iface->lookup = gtk_css_style_provider_lookup;
798
  iface->emit_error = gtk_css_style_provider_emit_error;
799 800
}

Carlos Garnacho's avatar
Carlos Garnacho committed
801 802 803
static void
gtk_css_provider_finalize (GObject *object)
{
804
  GtkCssProvider *css_provider;
Carlos Garnacho's avatar
Carlos Garnacho committed
805
  GtkCssProviderPrivate *priv;
806
  guint i;
Carlos Garnacho's avatar
Carlos Garnacho committed
807

808 809
  css_provider = GTK_CSS_PROVIDER (object);
  priv = css_provider->priv;
810

811 812 813
  for (i = 0; i < priv->rulesets->len; i++)
    gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));

814
  g_array_free (priv->rulesets, TRUE);
815
  _gtk_css_selector_tree_free (priv->tree);
Carlos Garnacho's avatar
Carlos Garnacho committed
816

817 818
  g_hash_table_destroy (priv->symbolic_colors);
  g_hash_table_destroy (priv->keyframes);
Carlos Garnacho's avatar
Carlos Garnacho committed
819

820 821 822 823 824 825 826
  if (priv->resource)
    {
      g_resources_unregister (priv->resource);
      g_resource_unref (priv->resource);
      priv->resource = NULL;
    }

Carlos Garnacho's avatar
Carlos Garnacho committed
827 828 829
  G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
830 831 832 833 834 835 836
/**
 * gtk_css_provider_new:
 *
 * Returns a newly created #GtkCssProvider.
 *
 * Returns: A new #GtkCssProvider
 **/
Carlos Garnacho's avatar
Carlos Garnacho committed
837 838 839 840 841 842
GtkCssProvider *
gtk_css_provider_new (void)
{
  return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
}

843 844
static void
gtk_css_provider_take_error (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
845
                             GtkCssScanner  *scanner,
846 847
                             GError         *error)
{
848
  gtk_css_provider_emit_error (provider, scanner, error);
849
  g_error_free (error);
850 851
}

852 853
static void
gtk_css_provider_error_literal (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
854
                                GtkCssScanner  *scanner,
855 856 857 858 859
                                GQuark          domain,
                                gint            code,
                                const char     *message)
{
  gtk_css_provider_take_error (provider,
860
                               scanner,
861 862 863
                               g_error_new_literal (domain, code, message));
}

864 865
static void
gtk_css_provider_error (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
866
                        GtkCssScanner  *scanner,
867 868 869
                        GQuark          domain,
                        gint            code,
                        const char     *format,
870
                        ...)  G_GNUC_PRINTF (5, 6);
871 872
static void
gtk_css_provider_error (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
873
                        GtkCssScanner  *scanner,
874 875 876 877 878
                        GQuark          domain,
                        gint            code,
                        const char     *format,
                        ...)
{
879
  GError *error;
880 881
  va_list args;

882 883 884
  gtk_internal_return_if_fail (GTK_IS_CSS_PROVIDER (provider));
  gtk_internal_return_if_fail (scanner != NULL);

885
  va_start (args, format);
886
  error = g_error_new_valist (domain, code, format, args);
887 888
  va_end (args);

889
  gtk_css_provider_take_error (provider, scanner, error);
890 891
}

892 893
static void
gtk_css_provider_invalid_token (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
894
                                GtkCssScanner  *scanner,
895 896
                                const char     *expected)
{
897
  gtk_css_provider_error (provider,
898
                          scanner,
899
                          GTK_CSS_PROVIDER_ERROR,
900
                          GTK_CSS_PROVIDER_ERROR_SYNTAX,
Benjamin Otte's avatar
Benjamin Otte committed
901
                          "expected %s", expected);
902 903
}

Benjamin Otte's avatar
Benjamin Otte committed
904
static void 
905
css_provider_commit (GtkCssProvider *css_provider,
Benjamin Otte's avatar
Benjamin Otte committed
906
                     GSList         *selectors,
907
                     GtkCssRuleset  *ruleset)
Carlos Garnacho's avatar
Carlos Garnacho committed
908 909 910 911
{
  GtkCssProviderPrivate *priv;
  GSList *l;

912
  priv = css_provider->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
913

914
  if (ruleset->styles == NULL && ruleset->widget_style == NULL)
Benjamin Otte's avatar
Benjamin Otte committed
915 916 917 918
    {
      g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
      return;
    }
919

Benjamin Otte's avatar
Benjamin Otte committed
920
  for (l = selectors; l; l = l->next)
Carlos Garnacho's avatar
Carlos Garnacho committed
921
    {
922
      GtkCssRuleset new;
Carlos Garnacho's avatar
Carlos Garnacho committed
923

924
      gtk_css_ruleset_init_copy (&new, ruleset, l->data);
Carlos Garnacho's avatar
Carlos Garnacho committed
925

926
      g_array_append_val (priv->rulesets, new);
Carlos Garnacho's avatar
Carlos Garnacho committed
927
    }
Benjamin Otte's avatar
Benjamin Otte committed
928

929
  g_slist_free (selectors);
Carlos Garnacho's avatar
Carlos Garnacho committed
930 931
}

Benjamin Otte's avatar
Benjamin Otte committed
932 933
static void
gtk_css_provider_reset (GtkCssProvider *css_provider)
Carlos Garnacho's avatar
Carlos Garnacho committed
934
{
Benjamin Otte's avatar
Benjamin Otte committed
935
  GtkCssProviderPrivate *priv;
936
  guint i;
937

Benjamin Otte's avatar
Benjamin Otte committed
938
  priv = css_provider->priv;
939

940 941 942 943 944 945 946
  if (priv->resource)
    {
      g_resources_unregister (priv->resource);
      g_resource_unref (priv->resource);
      priv->resource = NULL;
    }

947
  g_hash_table_remove_all (priv->symbolic_colors);
948
  g_hash_table_remove_all (priv->keyframes);
949

950 951 952
  for (i = 0; i < priv->rulesets->len; i++)
    gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
  g_array_set_size (priv->rulesets, 0);
953 954 955
  _gtk_css_selector_tree_free (priv->tree);
  priv->tree = NULL;

Benjamin Otte's avatar
Benjamin Otte committed
956
}
957

Benjamin Otte's avatar
Benjamin Otte committed
958 959
static void
gtk_css_provider_propagate_error (GtkCssProvider  *provider,
960
                                  GtkCssSection   *section,
Benjamin Otte's avatar