gtkcssprovider.c 69.4 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 "gtkutilsprivate.h"
48
#include "gtkversion.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
49

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

Matthias Clasen's avatar
Matthias Clasen committed
81

82
typedef struct GtkCssRuleset GtkCssRuleset;
Benjamin Otte's avatar
Benjamin Otte committed
83
typedef struct _GtkCssScanner GtkCssScanner;
84 85
typedef struct _PropertyValue PropertyValue;
typedef struct _WidgetPropertyValue WidgetPropertyValue;
Carlos Garnacho's avatar
Carlos Garnacho committed
86
typedef enum ParserScope ParserScope;
87
typedef enum ParserSymbol ParserSymbol;
Carlos Garnacho's avatar
Carlos Garnacho committed
88

89 90
struct _PropertyValue {
  GtkCssStyleProperty *property;
91
  GtkCssValue         *value;
92 93 94 95 96
  GtkCssSection       *section;
};

struct _WidgetPropertyValue {
  WidgetPropertyValue *next;
97 98
  char *name;
  char *value;
99 100 101 102

  GtkCssSection *section;
};

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

Benjamin Otte's avatar
Benjamin Otte committed
115
struct _GtkCssScanner
116
{
Benjamin Otte's avatar
Benjamin Otte committed
117 118
  GtkCssProvider *provider;
  GtkCssParser *parser;
119
  GtkCssSection *section;
Benjamin Otte's avatar
Benjamin Otte committed
120
  GtkCssScanner *parent;
121
  GSList *state;
122 123
};

124
struct _GtkCssProviderPrivate
Carlos Garnacho's avatar
Carlos Garnacho committed
125 126
{
  GScanner *scanner;
127

128
  GHashTable *symbolic_colors;
129
  GHashTable *keyframes;
130

131
  GArray *rulesets;
132
  GtkCssSelectorTree *tree;
133
  GResource *resource;
134
  gchar *path;
Carlos Garnacho's avatar
Carlos Garnacho committed
135 136
};

137 138 139 140 141
enum {
  PARSING_ERROR,
  LAST_SIGNAL
};

142 143
static gboolean gtk_keep_css_sections = FALSE;

144 145
static guint css_provider_signals[LAST_SIGNAL] = { 0 };

Carlos Garnacho's avatar
Carlos Garnacho committed
146 147
static void gtk_css_provider_finalize (GObject *object);
static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
148
static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface);
149
static void widget_property_value_list_free (WidgetPropertyValue *head);
150 151 152
static void gtk_css_style_provider_emit_error (GtkStyleProviderPrivate *provider,
                                               GtkCssSection           *section,
                                               const GError            *error);
Carlos Garnacho's avatar
Carlos Garnacho committed
153

154 155
static gboolean
gtk_css_provider_load_internal (GtkCssProvider *css_provider,
Benjamin Otte's avatar
Benjamin Otte committed
156
                                GtkCssScanner  *scanner,
157 158 159
                                GFile          *file,
                                const char     *data,
                                GError        **error);
160

Matthias Clasen's avatar
Matthias Clasen committed
161 162 163 164 165 166
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
167
G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
168
                        G_ADD_PRIVATE (GtkCssProvider)
Carlos Garnacho's avatar
Carlos Garnacho committed
169
                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
170 171 172
                                               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
173

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

192 193
      g_warning ("Theme parsing error: %s: %s",
                 s,
194 195
                 error->message);

196
      g_free (s);
197
    }
198 199
}

200 201 202 203 204 205 206 207 208
/* 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
209 210 211 212 213
static void
gtk_css_provider_class_init (GtkCssProviderClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

214
  if (g_getenv ("GTK_CSS_DEBUG"))
215
    gtk_css_provider_set_keep_css_sections ();
216

217 218 219
  /**
   * GtkCssProvider::parsing-error:
   * @provider: the provider that had a parsing error
220
   * @section: section the error happened in
221 222
   * @error: The parsing error
   *
Matthias Clasen's avatar
Matthias Clasen committed
223
   * Signals that a parsing error occurred. the @path, @line and @position
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
   * 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,
241 242
                  _gtk_marshal_VOID__BOXED_BOXED,
                  G_TYPE_NONE, 2, GTK_TYPE_CSS_SECTION, G_TYPE_ERROR);
243

Carlos Garnacho's avatar
Carlos Garnacho committed
244 245
  object_class->finalize = gtk_css_provider_finalize;

246
  klass->parsing_error = gtk_css_provider_parsing_error;
Carlos Garnacho's avatar
Carlos Garnacho committed
247 248
}

249 250
static void
gtk_css_ruleset_init_copy (GtkCssRuleset       *new,
251
                           GtkCssRuleset       *ruleset,
252
                           GtkCssSelector      *selector)
Carlos Garnacho's avatar
Carlos Garnacho committed
253
{
254
  memcpy (new, ruleset, sizeof (GtkCssRuleset));
Carlos Garnacho's avatar
Carlos Garnacho committed
255

256
  new->selector = selector;
257
  /* First copy takes over ownership */
258 259
  if (ruleset->owns_styles)
    ruleset->owns_styles = FALSE;
260 261
  if (ruleset->owns_widget_style)
    ruleset->owns_widget_style = FALSE;
262 263
  if (new->set_styles)
    new->set_styles = _gtk_bitmask_copy (new->set_styles);
Carlos Garnacho's avatar
Carlos Garnacho committed
264 265 266
}

static void
267
gtk_css_ruleset_clear (GtkCssRuleset *ruleset)
Carlos Garnacho's avatar
Carlos Garnacho committed
268
{
269 270 271 272 273 274
  if (ruleset->owns_styles)
    {
      guint i;

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

289
  memset (ruleset, 0, sizeof (GtkCssRuleset));
Carlos Garnacho's avatar
Carlos Garnacho committed
290 291
}

292 293 294 295 296 297 298 299
static WidgetPropertyValue *
widget_property_value_new (char *name, GtkCssSection *section)
{
  WidgetPropertyValue *value;

  value = g_slice_new0 (WidgetPropertyValue);

  value->name = name;
300 301
  if (gtk_keep_css_sections)
    value->section = gtk_css_section_ref (section);
302 303 304 305 306 307 308

  return value;
}

static void
widget_property_value_free (WidgetPropertyValue *value)
{
309
  g_free (value->value);
310
  g_free (value->name);
311 312
  if (value->section)
    gtk_css_section_unref (value->section);
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 345 346 347 348 349

  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;
}

350 351 352
static void
gtk_css_ruleset_add_style (GtkCssRuleset *ruleset,
                           char          *name,
353
                           WidgetPropertyValue *value)
354
{
355 356 357
  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
358 359
}

360
static void
Benjamin Otte's avatar
Benjamin Otte committed
361
gtk_css_ruleset_add (GtkCssRuleset       *ruleset,
362
                     GtkCssStyleProperty *property,
363
                     GtkCssValue         *value,
364
                     GtkCssSection       *section)
365
{
366 367 368
  guint i;

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

370 371
  if (ruleset->set_styles == NULL)
    ruleset->set_styles = _gtk_bitmask_new ();
372

373 374 375
  ruleset->set_styles = _gtk_bitmask_set (ruleset->set_styles,
                                          _gtk_css_style_property_get_id (property),
                                          TRUE);
376

377 378 379 380 381 382
  ruleset->owns_styles = TRUE;

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

398
  ruleset->styles[i].value = value;
399 400 401 402
  if (gtk_keep_css_sections)
    ruleset->styles[i].section = gtk_css_section_ref (section);
  else
    ruleset->styles[i].section = NULL;
403 404
}

Benjamin Otte's avatar
Benjamin Otte committed
405 406 407
static void
gtk_css_scanner_destroy (GtkCssScanner *scanner)
{
Benjamin Otte's avatar
Benjamin Otte committed
408 409
  if (scanner->section)
    gtk_css_section_unref (scanner->section);
Benjamin Otte's avatar
Benjamin Otte committed
410 411 412 413
  g_object_unref (scanner->provider);
  _gtk_css_parser_free (scanner->parser);

  g_slice_free (GtkCssScanner, scanner);
414 415
}

416 417 418 419 420 421 422 423
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);
}

424 425 426 427 428
static void
gtk_css_provider_emit_error (GtkCssProvider *provider,
                             GtkCssScanner  *scanner,
                             const GError   *error)
{
429 430 431
  gtk_css_style_provider_emit_error (GTK_STYLE_PROVIDER_PRIVATE (provider),
                                     scanner ? scanner->section : NULL,
                                     error);
432 433
}

434
static void
Benjamin Otte's avatar
Benjamin Otte committed
435 436 437
gtk_css_scanner_parser_error (GtkCssParser *parser,
                              const GError *error,
                              gpointer      user_data)
438
{
Benjamin Otte's avatar
Benjamin Otte committed
439
  GtkCssScanner *scanner = user_data;
440

441
  gtk_css_provider_emit_error (scanner->provider, scanner, error);
442 443
}

Benjamin Otte's avatar
Benjamin Otte committed
444 445 446
static GtkCssScanner *
gtk_css_scanner_new (GtkCssProvider *provider,
                     GtkCssScanner  *parent,
447
                     GtkCssSection  *section,
Benjamin Otte's avatar
Benjamin Otte committed
448
                     GFile          *file,
449
                     const gchar    *text)
Carlos Garnacho's avatar
Carlos Garnacho committed
450
{
Benjamin Otte's avatar
Benjamin Otte committed
451
  GtkCssScanner *scanner;
Carlos Garnacho's avatar
Carlos Garnacho committed
452

Benjamin Otte's avatar
Benjamin Otte committed
453
  scanner = g_slice_new0 (GtkCssScanner);
454

Benjamin Otte's avatar
Benjamin Otte committed
455 456 457
  g_object_ref (provider);
  scanner->provider = provider;
  scanner->parent = parent;
458 459
  if (section)
    scanner->section = gtk_css_section_ref (section);
460

461
  scanner->parser = _gtk_css_parser_new (text,
462
                                         file,
Benjamin Otte's avatar
Benjamin Otte committed
463 464
                                         gtk_css_scanner_parser_error,
                                         scanner);
465

466 467 468
  return scanner;
}

469
static gboolean
Benjamin Otte's avatar
Benjamin Otte committed
470 471
gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
                               GFile         *file)
472 473 474
{
  while (scanner)
    {
475 476
      GFile *parser_file = _gtk_css_parser_get_file (scanner->parser);
      if (parser_file && g_file_equal (parser_file, file))
477 478
        return TRUE;

Benjamin Otte's avatar
Benjamin Otte committed
479
      scanner = scanner->parent;
480 481 482 483 484
    }

  return FALSE;
}

Benjamin Otte's avatar
Benjamin Otte committed
485 486 487 488
static void
gtk_css_scanner_push_section (GtkCssScanner     *scanner,
                              GtkCssSectionType  section_type)
{
489
  GtkCssSection *section;
Benjamin Otte's avatar
Benjamin Otte committed
490

491
  section = _gtk_css_section_new (scanner->section,
Benjamin Otte's avatar
Benjamin Otte committed
492
                                  section_type,
493
                                  scanner->parser);
494

495 496
  if (scanner->section)
    gtk_css_section_unref (scanner->section);
497
  scanner->section = section;
Benjamin Otte's avatar
Benjamin Otte committed
498 499 500 501 502 503
}

static void
gtk_css_scanner_pop_section (GtkCssScanner *scanner,
                             GtkCssSectionType check_type)
{
504
  GtkCssSection *parent;
Benjamin Otte's avatar
Benjamin Otte committed
505
  
506 507 508 509 510
  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
511

512 513
  _gtk_css_section_end (scanner->section);
  gtk_css_section_unref (scanner->section);
Benjamin Otte's avatar
Benjamin Otte committed
514

515
  scanner->section = parent;
Benjamin Otte's avatar
Benjamin Otte committed
516 517
}

518 519 520 521 522
static void
gtk_css_provider_init (GtkCssProvider *css_provider)
{
  GtkCssProviderPrivate *priv;

523
  priv = css_provider->priv = gtk_css_provider_get_instance_private (css_provider);
524

525
  priv->rulesets = g_array_new (FALSE, FALSE, sizeof (GtkCssRuleset));
526 527 528

  priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                 (GDestroyNotify) g_free,
529
                                                 (GDestroyNotify) _gtk_css_value_unref);
530 531
  priv->keyframes = g_hash_table_new_full (g_str_hash, g_str_equal,
                                           (GDestroyNotify) g_free,
Matthias Clasen's avatar
Matthias Clasen committed
532
                                           (GDestroyNotify) _gtk_css_keyframes_unref);
Carlos Garnacho's avatar
Carlos Garnacho committed
533 534
}

535 536 537 538 539 540 541 542
static void
verify_tree_match_results (GtkCssProvider *provider,
			   const GtkCssMatcher *matcher,
			   GPtrArray *tree_rules)
{
#ifdef VERIFY_TREE
  GtkCssProviderPrivate *priv = provider->priv;
  GtkCssRuleset *ruleset;
543
  gboolean should_match;
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
  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;
	    }
	}
560 561
      should_match = _gtk_css_selector_matches (ruleset->selector, matcher);
      if (found != !!should_match)
562
	{
563
	  g_error ("expected rule '%s' to %s, but it %s",
564
		   _gtk_css_selector_to_string (ruleset->selector),
565
		   should_match ? "match" : "not match",
566 567 568 569 570 571
		   found ? "matched" : "didn't match");
	}
    }
#endif
}

572 573 574 575 576 577 578 579 580 581 582 583
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);
584
    if (tree_rules)
585
      {
586 587 588 589 590
        verify_tree_match_results (provider, matcher, tree_rules);

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

592
            ruleset = tree_rules->pdata[i];
593

594 595 596 597
            verify_change |= _gtk_css_selector_get_change (ruleset->selector);
          }

        g_ptr_array_free (tree_rules, TRUE);
598 599 600 601 602 603 604
      }

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

	s = g_string_new ("");
605 606 607 608
	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);
609
	if ((change & ~verify_change) != 0)
610 611 612 613
          {
	    g_string_append (s, ", unexpectedly set: ");
            gtk_css_change_print (change & ~verify_change, s);
          }
614
	if ((~change & verify_change) != 0)
615 616 617 618
          {
	    g_string_append_printf (s, ", unexpectedly not set: ");
            gtk_css_change_print (~change & verify_change, s);
          }
619 620 621 622 623 624 625 626
	g_warning (s->str);
	g_string_free (s, TRUE);
      }
  }
#endif
}


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

643 644 645 646 647 648 649 650 651 652
  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);
    }

653
  if (!_gtk_css_matcher_init (&matcher, path, NULL))
654 655 656 657
    {
      gtk_widget_path_unref (path);
      return FALSE;
    }
658

659
  tree_rules = _gtk_css_selector_tree_match_all (priv->tree, &matcher);
660
  if (tree_rules)
661
    {
662
      verify_tree_match_results (css_provider, &matcher, tree_rules);
663

664 665 666
      prop_name = g_strdup_printf ("-%s-%s",
                                   g_type_name (pspec->owner_type),
                                   pspec->name);
667

668 669 670
      for (i = tree_rules->len - 1; i >= 0; i--)
        {
          GtkCssRuleset *ruleset = tree_rules->pdata[i];
671

672 673
          if (ruleset->widget_style == NULL)
            continue;
674

675 676 677 678 679 680 681 682 683 684 685
          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);
686 687
                  if (!val->section)
                    gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
688
                  found = _gtk_css_style_funcs_parse_value (value, scanner->parser);
689 690
                  if (!val->section)
                    gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
691 692 693 694 695 696 697 698
                  gtk_css_scanner_destroy (scanner);
	          break;
                }
            }

          if (found)
            break;
        }
699

700 701
      g_free (prop_name);
      g_ptr_array_free (tree_rules, TRUE);
702 703
    }

704
  gtk_widget_path_unref (path);
705 706 707 708

  return found;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
709 710 711
static void
gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
{
712
  iface->get_style_property = gtk_css_provider_get_style_property;
Carlos Garnacho's avatar
Carlos Garnacho committed
713 714
}

715
static GtkCssValue *
716 717 718 719 720 721 722 723
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);
}

724 725 726 727 728 729 730 731 732
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);
}

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

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

749
  tree_rules = _gtk_css_selector_tree_match_all (priv->tree, matcher);
750
  if (tree_rules)
751
    {
752
      verify_tree_match_results (css_provider, matcher, tree_rules);
753

754
      for (i = tree_rules->len - 1; i >= 0; i--)
755
        {
756
          ruleset = tree_rules->pdata[i];
757

758
          if (ruleset->styles == NULL)
759
            continue;
760

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
          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;
781
        }
782

783
      g_ptr_array_free (tree_rules, TRUE);
784
    }
785

786 787 788
  if (change)
    {
      GtkCssMatcher change_matcher;
789

790
      _gtk_css_matcher_superset_init (&change_matcher, matcher, GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS);
791

792 793 794
      *change = _gtk_css_selector_tree_get_change_all (priv->tree, &change_matcher);
      verify_tree_get_change_results (css_provider, &change_matcher, *change);
    }
795 796
}

797 798 799 800
static void
gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface)
{
  iface->get_color = gtk_css_style_provider_get_color;
801
  iface->get_keyframes = gtk_css_style_provider_get_keyframes;
802
  iface->lookup = gtk_css_style_provider_lookup;
803
  iface->emit_error = gtk_css_style_provider_emit_error;
804 805
}

Carlos Garnacho's avatar
Carlos Garnacho committed
806 807 808
static void
gtk_css_provider_finalize (GObject *object)
{
809
  GtkCssProvider *css_provider;
Carlos Garnacho's avatar
Carlos Garnacho committed
810
  GtkCssProviderPrivate *priv;
811
  guint i;
Carlos Garnacho's avatar
Carlos Garnacho committed
812

813 814
  css_provider = GTK_CSS_PROVIDER (object);
  priv = css_provider->priv;
815

816 817 818
  for (i = 0; i < priv->rulesets->len; i++)
    gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));

819
  g_array_free (priv->rulesets, TRUE);
820
  _gtk_css_selector_tree_free (priv->tree);
Carlos Garnacho's avatar
Carlos Garnacho committed
821

822 823
  g_hash_table_destroy (priv->symbolic_colors);
  g_hash_table_destroy (priv->keyframes);
Carlos Garnacho's avatar
Carlos Garnacho committed
824

825 826 827 828 829 830 831
  if (priv->resource)
    {
      g_resources_unregister (priv->resource);
      g_resource_unref (priv->resource);
      priv->resource = NULL;
    }

832 833
  g_free (priv->path);

Carlos Garnacho's avatar
Carlos Garnacho committed
834 835 836
  G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
837 838 839 840 841 842 843
/**
 * gtk_css_provider_new:
 *
 * Returns a newly created #GtkCssProvider.
 *
 * Returns: A new #GtkCssProvider
 **/
Carlos Garnacho's avatar
Carlos Garnacho committed
844 845 846 847 848 849
GtkCssProvider *
gtk_css_provider_new (void)
{
  return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
}

850 851
static void
gtk_css_provider_take_error (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
852
                             GtkCssScanner  *scanner,
853 854
                             GError         *error)
{
855
  gtk_css_provider_emit_error (provider, scanner, error);
856
  g_error_free (error);
857 858
}

859 860
static void
gtk_css_provider_error_literal (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
861
                                GtkCssScanner  *scanner,
862 863 864 865 866
                                GQuark          domain,
                                gint            code,
                                const char     *message)
{
  gtk_css_provider_take_error (provider,
867
                               scanner,
868 869 870
                               g_error_new_literal (domain, code, message));
}

871 872
static void
gtk_css_provider_error (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
873
                        GtkCssScanner  *scanner,
874 875 876
                        GQuark          domain,
                        gint            code,
                        const char     *format,
877
                        ...)  G_GNUC_PRINTF (5, 6);
878 879
static void
gtk_css_provider_error (GtkCssProvider *provider,
Benjamin Otte's avatar
Benjamin Otte committed
880
                        GtkCssScanner  *scanner,
881 882 883 884 885
                        GQuark          domain,
                        gint            code,
                        const char     *format,
                        ...)
{
886
  GError *error;
887 888
  va_list args;

889 890 891
  gtk_internal_return_if_fail (GTK_IS_CSS_PROVIDER (provider));
  gtk_internal_return_if_fail (scanner != NULL);

892
  va_start (args, format);
893
  error = g_error_new_valist (domain, code, format, args);
894 895
  va_end (args);

896
  gtk_css_provider_take_error (provider, scanner, error);
897 898