Commit 00d14e35 authored by Benjamin Otte's avatar Benjamin Otte

API: css: Add GtkCssSection

This struct keeps track of an area of text in a CSS file and uses it
when specifying information. Also, the cssprovider keeps track of
sections when parsing a file.
parent c8fdd5e8
......@@ -5739,6 +5739,19 @@ gtk_css_provider_new
gtk_css_provider_to_string
GTK_CSS_PROVIDER_ERROR
GtkCssProviderError
<SUBSECTION>
GtkCssSection
GtkCssSectionType
gtk_css_section_get_end_line
gtk_css_section_get_end_position
gtk_css_section_get_file
gtk_css_section_get_parent
gtk_css_section_get_section_type
gtk_css_section_get_start_line
gtk_css_section_get_start_position
gtk_css_section_get_type
gtk_css_section_ref
gtk_css_section_unref
<SUBSECTION Standard>
GTK_TYPE_CSS_PROVIDER
GTK_CSS_PROVIDER
......@@ -5749,6 +5762,7 @@ GTK_IS_CSS_PROVIDER_CLASS
<SUBSECTION Private>
gtk_css_provider_get_type
gtk_css_provider_error_quark
gtk_css_section_get_type
</SECTION>
<SECTION>
......
......@@ -199,6 +199,7 @@ gtk_public_h_sources = \
gtkcomboboxtext.h \
gtkcontainer.h \
gtkcssprovider.h \
gtkcsssection.h \
gtkdebug.h \
gtkdialog.h \
gtkdnd.h \
......@@ -394,6 +395,7 @@ gtk_private_h_sources = \
gtkcontainerprivate.h \
gtkcssparserprivate.h \
gtkcssproviderprivate.h \
gtkcsssectionprivate.h \
gtkcssselectorprivate.h \
gtkcsstypesprivate.h \
gtkcustompaperunixdialog.h \
......@@ -526,6 +528,7 @@ gtk_base_c_sources = \
gtkcontainer.c \
gtkcssparser.c \
gtkcssprovider.c \
gtkcsssection.c \
gtkcssselector.c \
gtkcsstypes.c \
gtkdialog.c \
......
......@@ -82,6 +82,7 @@
#include <gtk/gtkcomboboxtext.h>
#include <gtk/gtkcontainer.h>
#include <gtk/gtkcssprovider.h>
#include <gtk/gtkcsssection.h>
#include <gtk/gtkdebug.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkdnd.h>
......
......@@ -701,6 +701,16 @@ gtk_css_provider_load_from_file
gtk_css_provider_load_from_path
gtk_css_provider_new
gtk_css_provider_to_string
gtk_css_section_get_end_line
gtk_css_section_get_end_position
gtk_css_section_get_file
gtk_css_section_get_parent
gtk_css_section_get_section_type
gtk_css_section_get_start_line
gtk_css_section_get_start_position
gtk_css_section_get_type
gtk_css_section_ref
gtk_css_section_unref
#ifdef G_OS_UNIX
gtk_custom_paper_unix_dialog_get_type
#endif
......
......@@ -28,6 +28,7 @@
#include "gtkcssproviderprivate.h"
#include "gtkcssparserprivate.h"
#include "gtkcsssectionprivate.h"
#include "gtkcssselectorprivate.h"
#include "gtksymboliccolor.h"
#include "gtkstyleprovider.h"
......@@ -964,6 +965,7 @@ struct _GtkCssScanner
{
GtkCssProvider *provider;
GtkCssParser *parser;
GSList *sections;
GtkCssScanner *parent;
GFile *file;
GFile *base;
......@@ -1237,6 +1239,8 @@ gtk_css_scanner_reset (GtkCssScanner *scanner)
static void
gtk_css_scanner_destroy (GtkCssScanner *scanner)
{
g_assert (scanner->sections == NULL);
gtk_css_scanner_reset (scanner);
g_object_unref (scanner->provider);
......@@ -1325,6 +1329,40 @@ gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
return FALSE;
}
static void
gtk_css_scanner_push_section (GtkCssScanner *scanner,
GtkCssSectionType section_type)
{
GtkCssSection *parent, *section;
if (scanner->sections)
parent = scanner->sections->data;
else if (scanner->parent)
parent = scanner->parent->sections->data;
else
parent = NULL;
section = _gtk_css_section_new (parent,
section_type,
scanner->parser,
scanner->file);
scanner->sections = g_slist_prepend (scanner->sections, section);
}
static void
gtk_css_scanner_pop_section (GtkCssScanner *scanner,
GtkCssSectionType check_type)
{
GtkCssSection *section = scanner->sections->data;
g_assert (check_type == gtk_css_section_get_section_type (section));
scanner->sections = g_slist_delete_link (scanner->sections, scanner->sections);
_gtk_css_section_end (section);
gtk_css_section_unref (section);
}
static void
gtk_css_provider_init (GtkCssProvider *css_provider)
{
......@@ -1664,20 +1702,30 @@ gtk_css_provider_propagate_error (GtkCssProvider *provider,
g_prefix_error (propagate_to, "%s:%u:%u: ", path ? path : "<unknown>", line, position);
}
static void
static gboolean
parse_import (GtkCssScanner *scanner)
{
GFile *file;
char *uri;
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_IMPORT);
if (!_gtk_css_parser_try (scanner->parser, "@import", TRUE))
{
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
return FALSE;
}
if (_gtk_css_parser_is_string (scanner->parser))
uri = _gtk_css_parser_read_string (scanner->parser);
else
uri = _gtk_css_parser_read_uri (scanner->parser);
if (uri == NULL)
{
_gtk_css_parser_resync (scanner->parser, TRUE, 0);
return;
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
return TRUE;
}
file = g_file_resolve_relative_path (gtk_css_scanner_get_base_url (scanner), uri);
......@@ -1705,21 +1753,30 @@ parse_import (GtkCssScanner *scanner)
if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
{
g_object_unref (file);
gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon");
_gtk_css_parser_resync (scanner->parser, TRUE, 0);
return;
}
g_object_unref (file);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
return TRUE;
}
static void
static gboolean
parse_color_definition (GtkCssScanner *scanner)
{
GtkSymbolicColor *symbolic;
char *name;
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
if (!_gtk_css_parser_try (scanner->parser, "@define-color", TRUE))
{
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
return FALSE;
}
name = _gtk_css_parser_try_name (scanner->parser, TRUE);
if (name == NULL)
{
......@@ -1729,7 +1786,8 @@ parse_color_definition (GtkCssScanner *scanner)
GTK_CSS_PROVIDER_ERROR_SYNTAX,
"Not a valid color name");
_gtk_css_parser_resync (scanner->parser, TRUE, 0);
return;
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
return TRUE;
}
symbolic = _gtk_css_parser_read_symbolic_color (scanner->parser);
......@@ -1737,7 +1795,8 @@ parse_color_definition (GtkCssScanner *scanner)
{
g_free (name);
_gtk_css_parser_resync (scanner->parser, TRUE, 0);
return;
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
return TRUE;
}
if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
......@@ -1750,18 +1809,31 @@ parse_color_definition (GtkCssScanner *scanner)
GTK_CSS_PROVIDER_ERROR_SYNTAX,
"Missing semicolon at end of color definition");
_gtk_css_parser_resync (scanner->parser, TRUE, 0);
return;
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
return TRUE;
}
g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, symbolic);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
return TRUE;
}
static void
static gboolean
parse_binding_set (GtkCssScanner *scanner)
{
GtkBindingSet *binding_set;
char *name;
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_BINDING_SET);
if (!_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE))
{
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_BINDING_SET);
return FALSE;
}
name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
if (name == NULL)
{
......@@ -1850,17 +1922,22 @@ skip_semicolon:
"Nonstandard semicolon at end of binding set");
_gtk_css_parser_try (scanner->parser, ";", TRUE);
}
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_BINDING_SET);
return TRUE;
}
static void
parse_at_keyword (GtkCssScanner *scanner)
{
if (_gtk_css_parser_try (scanner->parser, "@import", TRUE))
parse_import (scanner);
else if (_gtk_css_parser_try (scanner->parser, "@define-color", TRUE))
parse_color_definition (scanner);
else if (_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE))
parse_binding_set (scanner);
if (parse_import (scanner))
return;
if (parse_color_definition (scanner))
return;
if (parse_binding_set (scanner))
return;
else
{
gtk_css_provider_error_literal (scanner->provider,
......@@ -2152,6 +2229,8 @@ parse_selector_list (GtkCssScanner *scanner)
{
GSList *selectors = NULL;
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_SELECTOR);
do {
GtkCssSelector *select = parse_selector (scanner);
......@@ -2159,6 +2238,7 @@ parse_selector_list (GtkCssScanner *scanner)
{
g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
_gtk_css_parser_resync (scanner->parser, FALSE, 0);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR);
return NULL;
}
......@@ -2166,6 +2246,8 @@ parse_selector_list (GtkCssScanner *scanner)
}
while (_gtk_css_parser_try (scanner->parser, ",", TRUE));
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR);
return selectors;
}
......@@ -2176,6 +2258,8 @@ parse_declaration (GtkCssScanner *scanner,
const GtkStyleProperty *property;
char *name;
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DECLARATION);
name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
if (name == NULL)
goto check_for_semicolon;
......@@ -2191,6 +2275,7 @@ parse_declaration (GtkCssScanner *scanner,
name);
_gtk_css_parser_resync (scanner->parser, TRUE, '}');
g_free (name);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
return;
}
......@@ -2199,6 +2284,7 @@ parse_declaration (GtkCssScanner *scanner,
gtk_css_provider_invalid_token (scanner->provider, scanner, "':'");
_gtk_css_parser_resync (scanner->parser, TRUE, '}');
g_free (name);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
return;
}
......@@ -2232,6 +2318,7 @@ parse_declaration (GtkCssScanner *scanner,
_gtk_css_parser_resync (scanner->parser, TRUE, '}');
g_value_unset (val);
g_slice_free (GValue, val);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
return;
}
}
......@@ -2240,6 +2327,7 @@ parse_declaration (GtkCssScanner *scanner,
g_value_unset (val);
g_slice_free (GValue, val);
_gtk_css_parser_resync (scanner->parser, TRUE, '}');
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
return;
}
}
......@@ -2261,6 +2349,7 @@ parse_declaration (GtkCssScanner *scanner,
else
{
_gtk_css_parser_resync (scanner->parser, TRUE, '}');
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
return;
}
}
......@@ -2268,6 +2357,8 @@ parse_declaration (GtkCssScanner *scanner,
g_free (name);
check_for_semicolon:
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
{
if (!_gtk_css_parser_begins_with (scanner->parser, '}') &&
......@@ -2300,9 +2391,14 @@ parse_ruleset (GtkCssScanner *scanner)
GSList *selectors;
GtkCssRuleset ruleset = { 0, };
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_RULESET);
selectors = parse_selector_list (scanner);
if (selectors == NULL)
return;
{
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
return;
}
if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
{
......@@ -2313,6 +2409,7 @@ parse_ruleset (GtkCssScanner *scanner)
"expected '{' after selectors");
_gtk_css_parser_resync (scanner->parser, FALSE, 0);
g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
return;
}
......@@ -2330,12 +2427,13 @@ parse_ruleset (GtkCssScanner *scanner)
_gtk_css_parser_resync (scanner->parser, FALSE, 0);
g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
gtk_css_ruleset_clear (&ruleset);
return;
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
}
}
css_provider_commit (scanner->provider, selectors, &ruleset);
gtk_css_ruleset_clear (&ruleset);
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
}
static void
......@@ -2350,6 +2448,8 @@ parse_statement (GtkCssScanner *scanner)
static void
parse_stylesheet (GtkCssScanner *scanner)
{
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DOCUMENT);
_gtk_css_parser_skip_whitespace (scanner->parser);
while (!_gtk_css_parser_is_eof (scanner->parser))
......@@ -2360,6 +2460,8 @@ parse_stylesheet (GtkCssScanner *scanner)
parse_statement (scanner);
}
gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DOCUMENT);
}
static int
......
/* GTK - The GIMP Toolkit
* Copyright (C) 2011 Red Hat, Inc.
*
* 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 "gtkcsssectionprivate.h"
#include "gtkcssparserprivate.h"
/**
* GtkCssSection:
*
* Defines a part of a CSS document. Because sections are nested into
* one another, you can use gtk_css_section_get_parent() to get the
* containing region.
*
* Since: 3.2
*/
struct _GtkCssSection
{
volatile gint ref_count;
GtkCssSectionType section_type;
GtkCssSection *parent;
GFile *file;
guint start_line;
guint start_position;
GtkCssParser *parser; /* parser if section isn't finished parsing yet or %NULL */
guint end_line; /* end line if parser is %NULL */
guint end_position; /* end position if parser is %NULL */
};
G_DEFINE_BOXED_TYPE (GtkCssSection, gtk_css_section, gtk_css_section_ref, gtk_css_section_unref)
GtkCssSection *
_gtk_css_section_new (GtkCssSection *parent,
GtkCssSectionType type,
GtkCssParser *parser,
GFile *file)
{
GtkCssSection *section;
g_return_val_if_fail (parser != NULL, NULL);
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
section = g_slice_new0 (GtkCssSection);
section->ref_count = 1;
section->section_type = type;
if (parent)
section->parent = gtk_css_section_ref (parent);
if (file)
section->file = g_object_ref (file);
section->start_line = _gtk_css_parser_get_line (parser);
section->start_position = _gtk_css_parser_get_position (parser);
section->parser = parser;
return section;
}
void
_gtk_css_section_end (GtkCssSection *section)
{
g_return_if_fail (section != NULL);
g_return_if_fail (section->parser != NULL);
section->end_line = _gtk_css_parser_get_line (section->parser);
section->end_position = _gtk_css_parser_get_position (section->parser);
section->parser = NULL;
}
/**
* gtk_css_section_ref:
* @section: a #GtkCssSection
*
* Increments the reference count on @section.
*
* Returns: @section itself.
*
* Since: 3.2
**/
GtkCssSection *
gtk_css_section_ref (GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, NULL);
g_atomic_int_add (&section->ref_count, 1);
return section;
}
/**
* gtk_css_section_unref:
* @section: a #GtkCssSection
*
* Decrements the reference count on @section, freeing the
* structure if the reference count reaches 0.
*
* Since: 3.2
**/
void
gtk_css_section_unref (GtkCssSection *section)
{
g_return_if_fail (section != NULL);
if (!g_atomic_int_dec_and_test (&section->ref_count))
return;
if (section->parent)
gtk_css_section_unref (section->parent);
if (section->file)
g_object_unref (section->file);
g_slice_free (GtkCssSection, section);
}
/**
* gtk_css_section_get_section_type:
* @section: the section
*
* Gets the type of information that @section describes.
*
* Returns: the type of @section
*
* Since: 3.2
**/
GtkCssSectionType
gtk_css_section_get_section_type (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, GTK_CSS_SECTION_DOCUMENT);
return section->section_type;
}
/**
* gtk_css_section_get_parent:
* @section: the section
*
* Gets the parent section for the given @section. The parent section is
* the section that contains this @section. A special case are sections of
* type #GTK_CSS_SECTION_TYPE_DOCUMENT. Their parent will either be %NULL
* if they are the original CSS document that was loaded by
* gtk_css_provider_load_from_file() or a section of type
* #GTK_CSS_SECTION_TYPE_IMPORT if it was loaded with an import rule from
* a different file.
*
* Returns: the parent section or %NULL if none
*
* Since: 3.2
**/
GtkCssSection *
gtk_css_section_get_parent (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, NULL);
return section->parent;
}
/**
* gtk_css_section_get_file:
* @section: the section
*
* Gets the file that @section was parsed from. If no such file exists,
* for example because the CSS was loaded via
* @gtk_css_provider_load_from_data(), then %NULL is returned.
*
* Returns: the #GFile that @section was parsed from or %NULL if
* @section was parsed from other data.
*
* Since: 3.2
**/
GFile *
gtk_css_section_get_file (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, NULL);
return section->file;
}
/**
* gtk_css_section_get_start_line:
* @section: the section
*
* Returns the line in the CSS document where this section starts.
* The line number is 0-indexed, so the first line of the document
* will return 0.
*
* Returns: the line number
*
* Since: 3.2
**/
guint
gtk_css_section_get_start_line (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, 0);
return section->start_line;
}
/**
* gtk_css_section_get_start_position:
* @section: the section
*
* Returns the offset in bytes from the start of the current line
* returned via gtk_css_section_get_start_line().
*
* Returns: the offset in bytes from the start of the line.
*
* Since: 3.2
**/
guint
gtk_css_section_get_start_position (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, 0);
return section->start_position;
}
/**
* gtk_css_section_get_end_line:
* @section: the section
*
* Returns the line in the CSS document where this section end.
* The line number is 0-indexed, so the first line of the document
* will return 0.
* This value may change in future invocations of this function if
* @section is not yet parsed completely. This will for example
* happen in the GtkCssProvider::parsing-error signal.
* The end position and line may be identical to the start
* position and line for sections which failed to parse anything
* successfully.
*
* Returns: the line number
*
* Since: 3.2
**/
guint
gtk_css_section_get_end_line (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, 0);
if (section->parser)
return _gtk_css_parser_get_line (section->parser);
else
return section->end_line;
}
/**
* gtk_css_section_get_start_position:
* @section: the section
*
* Returns the offset in bytes from the start of the current line
* returned via gtk_css_section_get_end_line().
* This value may change in future invocations of this function if
* @section is not yet parsed completely. This will for example
* happen in the GtkCssProvider::parsing-error signal.
* The end position and line may be identical to the start
* position and line for sections which failed to parse anything
* successfully.
*
* Returns: the offset in bytes from the start of the line.
*
* Since: 3.2
**/
guint
gtk_css_section_get_end_position (const GtkCssSection *section)
{
g_return_val_if_fail (section != NULL, 0);
if (section->parser)
return _gtk_css_parser_get_position (section->parser);
else
return section->end_position;
}
/* GTK - The GIMP Toolkit
* Copyright (C) 2011 Red Hat, Inc.
*
* 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.
*/
#ifndef __GTK_CSS_SECTION_H__
#define __GTK_CSS_SECTION_H__
#include <gio/gio.h>
G_BEGIN_DECLS
#define GTK_TYPE_CSS_SECTION (gtk_css_section_get_type ())
/**
* GtkCssSection:
* @GTK_CSS_SECTION_DOCUMENT: The section describes a complete document.
* This section time is the only one where gtk_css_section_get_parent()
* might return %NULL.
* @GTK_CSS_SECTION_IMPORT: The section defines an import rule.
* @GTK_CSS_SECTION_COLOR_DEFINITION: The section defines a color. This
* is a GTK extension to CSS.
* @GTK_CSS_SECTION_BINDING_SET: The section defines a binding set. This
* is a GTK extension to CSS.
* @GTK_CSS_SECTION_RULESET: The section defines a CSS ruleset.
* @GTK_CSS_SECTION_SELECTOR: The section defines a CSS selector.
* @GTK_CSS_SECTION_DECLARATION: The section defines the declaration of
* a CSS variable.
*
* The different types of sections indicate parts of a CSS document as
* parsed by GTK's CSS parser. They are oriented towards the CSS grammar
* <ulink url="http://www.w3.org/TR/CSS21/grammar.html">CSS grammer</ulink>,
* but may contain extensions.
*
* More types might be added in the future as the parser incorporates
* more features.
*