[gtk+] css: Rewrite selectors
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] css: Rewrite selectors
- Date: Fri, 2 Mar 2012 01:24:58 +0000 (UTC)
commit 35a0fb09acb1cbf1a1e99f33d73597c7d46173ea
Author: Benjamin Otte <otte redhat com>
Date: Thu Feb 16 15:16:18 2012 +0100
css: Rewrite selectors
Previously we kept a Selector object for every "simple selector" (term
from CSS spec). Now we keep one for every match operation. So given the
selector
".a b:focus"
we will have 4 elements:
- pseudoclass ":focus"
- element "b"
- match any desendant (the space)
- class ".a"
Each of those is represented by a "selector class" which is basically
the collection of vfuncs for this selector.
gtk/gtkcssselector.c | 988 ++++++++++++++++++++-----------------
tests/css/parser/selector.ref.css | 12 +-
2 files changed, 542 insertions(+), 458 deletions(-)
---
diff --git a/gtk/gtkcssselector.c b/gtk/gtkcssselector.c
index 96f010a..20aec45 100644
--- a/gtk/gtkcssselector.c
+++ b/gtk/gtkcssselector.c
@@ -22,51 +22,437 @@
#include "gtkcssprovider.h"
#include "gtkstylecontextprivate.h"
-typedef enum {
- GTK_CSS_COMBINE_DESCANDANT,
- GTK_CSS_COMBINE_CHILD
-} GtkCssCombinator;
+typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
+
+struct _GtkCssSelectorClass {
+ const char *name;
+
+ void (* print) (const GtkCssSelector *selector,
+ GString *string);
+ gboolean (* match) (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id);
+
+ guint increase_id_specificity :1;
+ guint increase_class_specificity :1;
+ guint increase_element_specificity :1;
+};
struct _GtkCssSelector
{
- GtkCssSelector * previous; /* link to next element in selector or NULL if last */
- GtkCssCombinator combine; /* how to combine with the previous element */
- const char * name; /* quarked name of element we match or NULL if any */
- GType type; /* cache for type belonging to name - G_TYPE_INVALID if uncached */
- GQuark * ids; /* 0-terminated list of required ids or NULL if none */
- GQuark * classes; /* 0-terminated list of required classes or NULL if none */
- GtkRegionFlags pseudo_classes; /* required pseudo classes */
- GtkStateFlags state; /* required state flags (currently not checked when matching) */
+ const GtkCssSelectorClass *class; /* type of check this selector does */
+ GtkCssSelector *previous; /* link to next element in selector or NULL if last */
+ gconstpointer data; /* data for matching:
+ - interned string for CLASS, NAME and ID
+ - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
+};
+
+static gboolean
+gtk_css_selector_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ if (selector == NULL)
+ return TRUE;
+
+ return selector->class->match (selector, state, path, id);
+}
+
+/* ANY */
+
+static void
+gtk_css_selector_any_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, '*');
+}
+
+static gboolean
+gtk_css_selector_any_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
+ "any",
+ gtk_css_selector_any_print,
+ gtk_css_selector_any_match,
+ FALSE, FALSE, FALSE
+};
+
+/* DESCENDANT */
+
+static void
+gtk_css_selector_descendant_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, ' ');
+}
+
+static gboolean
+gtk_css_selector_descendant_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ while (id-- > 0)
+ {
+ if (gtk_css_selector_match (selector->previous, 0, path, id))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
+ "descendant",
+ gtk_css_selector_descendant_print,
+ gtk_css_selector_descendant_match,
+ FALSE, FALSE, FALSE
+};
+
+/* CHILD */
+
+static void
+gtk_css_selector_child_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, " > ");
+}
+
+static gboolean
+gtk_css_selector_child_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ if (id == 0)
+ return FALSE;
+
+ return gtk_css_selector_match (selector->previous, 0, path, id - 1);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
+ "child",
+ gtk_css_selector_child_print,
+ gtk_css_selector_child_match,
+ FALSE, FALSE, FALSE
+};
+
+/* NAME */
+
+static void
+gtk_css_selector_name_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, selector->data);
+}
+
+static gboolean
+gtk_css_selector_name_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ GType type = g_type_from_name (selector->data);
+
+ if (!g_type_is_a (gtk_widget_path_iter_get_object_type (path, id), type))
+ return FALSE;
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
+ "name",
+ gtk_css_selector_name_print,
+ gtk_css_selector_name_match,
+ FALSE, FALSE, TRUE
+};
+
+/* REGION */
+
+static void
+gtk_css_selector_region_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append (string, selector->data);
+}
+
+static gboolean
+gtk_css_selector_region_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ if (!gtk_widget_path_iter_has_region (path, id, selector->data, NULL))
+ return FALSE;
+
+ if (selector->previous &&
+ selector->previous->class == >K_CSS_SELECTOR_DESCENDANT &&
+ gtk_css_selector_match (selector->previous->previous, state, path, id))
+ return TRUE;
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
+ "region",
+ gtk_css_selector_region_print,
+ gtk_css_selector_region_match,
+ FALSE, FALSE, TRUE
+};
+
+/* CLASS */
+
+static void
+gtk_css_selector_class_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, '.');
+ g_string_append (string, selector->data);
+}
+
+static gboolean
+gtk_css_selector_class_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ if (!gtk_widget_path_iter_has_class (path, id, selector->data))
+ return FALSE;
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
+ "class",
+ gtk_css_selector_class_print,
+ gtk_css_selector_class_match,
+ FALSE, TRUE, FALSE
+};
+
+/* ID */
+
+static void
+gtk_css_selector_id_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ g_string_append_c (string, '#');
+ g_string_append (string, selector->data);
+}
+
+static gboolean
+gtk_css_selector_id_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ if (!gtk_widget_path_iter_has_name (path, id, selector->data))
+ return FALSE;
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
+ "id",
+ gtk_css_selector_id_print,
+ gtk_css_selector_id_match,
+ TRUE, FALSE, FALSE
+};
+
+/* PSEUDOCLASS FOR STATE */
+
+static void
+gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ static const char * state_names[] = {
+ "active",
+ "hover",
+ "selected",
+ "insensitive",
+ "inconsistent",
+ "focus",
+ "backdrop"
+ };
+ guint i, state;
+
+ state = GPOINTER_TO_UINT (selector->data);
+ g_string_append_c (string, ':');
+
+ for (i = 0; i < G_N_ELEMENTS (state_names); i++)
+ {
+ if (state == (1 << i))
+ {
+ g_string_append (string, state_names[i]);
+ return;
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ if (!(GPOINTER_TO_UINT (selector->data) & state))
+ return FALSE;
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
+ "pseudoclass-state",
+ gtk_css_selector_pseudoclass_state_print,
+ gtk_css_selector_pseudoclass_state_match,
+ FALSE, TRUE, FALSE
};
+/* PSEUDOCLASS FOR REGION */
+
+static void
+gtk_css_selector_pseudoclass_region_print (const GtkCssSelector *selector,
+ GString *string)
+{
+ static const char * flag_names[] = {
+ "nth-child(even)",
+ "nth-child(odd)",
+ "first-child",
+ "last-child",
+ "only-child",
+ "sorted"
+ };
+ guint i, state;
+
+ state = GPOINTER_TO_UINT (selector->data);
+ g_string_append_c (string, ':');
+
+ for (i = 0; i < G_N_ELEMENTS (flag_names); i++)
+ {
+ if (state == (1 << i))
+ {
+ g_string_append (string, flag_names[i]);
+ return;
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+gtk_css_selector_pseudoclass_region_match_for_region (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ GtkRegionFlags selector_flags, path_flags;
+
+ selector_flags = GPOINTER_TO_UINT (selector->data);
+ selector = selector->previous;
+
+ if (!gtk_widget_path_iter_has_region (path, id, selector->data, &path_flags))
+ return FALSE;
+
+ if ((selector_flags & path_flags) != selector_flags)
+ return FALSE;
+
+ if (selector->previous &&
+ selector->previous->class == >K_CSS_SELECTOR_DESCENDANT &&
+ gtk_css_selector_match (selector->previous->previous, state, path, id))
+ return TRUE;
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static gboolean
+gtk_css_selector_pseudoclass_region_match (const GtkCssSelector *selector,
+ GtkStateFlags state,
+ const GtkWidgetPath *path,
+ guint id)
+{
+ GtkRegionFlags region;
+ const GtkWidgetPath *siblings;
+ guint sibling_id, n_siblings;
+
+ if (selector->previous &&
+ selector->previous->class == >K_CSS_SELECTOR_REGION)
+ return gtk_css_selector_pseudoclass_region_match_for_region (selector, state, path, id);
+
+ siblings = gtk_widget_path_iter_get_siblings (path, id);
+ if (siblings == NULL)
+ return 0;
+
+ region = GPOINTER_TO_UINT (selector->data);
+ sibling_id = gtk_widget_path_iter_get_sibling_index (path, id);
+ n_siblings = gtk_widget_path_length (siblings);
+
+ switch (region)
+ {
+ case GTK_REGION_EVEN:
+ if (!(sibling_id % 2))
+ return FALSE;
+ break;
+ case GTK_REGION_ODD:
+ if (sibling_id % 2)
+ return FALSE;
+ break;
+ case GTK_REGION_FIRST:
+ if (sibling_id != 0)
+ return FALSE;
+ break;
+ case GTK_REGION_LAST:
+ if (sibling_id + 1 != n_siblings)
+ return FALSE;
+ break;
+ case GTK_REGION_ONLY:
+ if (n_siblings != 1)
+ return FALSE;
+ break;
+ case GTK_REGION_SORTED:
+ return FALSE;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ return gtk_css_selector_match (selector->previous, state, path, id);
+}
+
+static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_REGION = {
+ "pseudoclass-region",
+ gtk_css_selector_pseudoclass_region_print,
+ gtk_css_selector_pseudoclass_region_match,
+ FALSE, TRUE, FALSE
+};
+
+/* API */
+
static GtkCssSelector *
-gtk_css_selector_new (GtkCssSelector *previous,
- GtkCssCombinator combine,
- const char * name,
- GQuark * ids,
- GQuark * classes,
- GtkRegionFlags pseudo_classes,
- GtkStateFlags state)
+gtk_css_selector_new (const GtkCssSelectorClass *class,
+ GtkCssSelector *previous,
+ gconstpointer data)
{
GtkCssSelector *selector;
selector = g_slice_new0 (GtkCssSelector);
+ selector->class = class;
selector->previous = previous;
- selector->combine = combine;
- selector->name = name ? g_quark_to_string (g_quark_from_string (name)) : NULL;
- selector->type = !name || _gtk_style_context_check_region_name (name) ? G_TYPE_NONE : G_TYPE_INVALID;
- selector->ids = ids;
- selector->classes = classes;
- selector->pseudo_classes = pseudo_classes;
- selector->state = state;
+ selector->data = data;
return selector;
}
-static gboolean
-parse_selector_class (GtkCssParser *parser, GArray *classes)
+static GtkCssSelector *
+parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
{
- GQuark qname;
char *name;
name = _gtk_css_parser_try_name (parser, FALSE);
@@ -74,19 +460,23 @@ parse_selector_class (GtkCssParser *parser, GArray *classes)
if (name == NULL)
{
_gtk_css_parser_error (parser, "Expected a valid name for class");
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ return NULL;
}
- qname = g_quark_from_string (name);
- g_array_append_val (classes, qname);
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
+ selector,
+ g_intern_string (name));
+
g_free (name);
- return TRUE;
+
+ return selector;
}
-static gboolean
-parse_selector_name (GtkCssParser *parser, GArray *names)
+static GtkCssSelector *
+parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
{
- GQuark qname;
char *name;
name = _gtk_css_parser_try_name (parser, FALSE);
@@ -94,19 +484,23 @@ parse_selector_name (GtkCssParser *parser, GArray *names)
if (name == NULL)
{
_gtk_css_parser_error (parser, "Expected a valid name for id");
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ return NULL;
}
- qname = g_quark_from_string (name);
- g_array_append_val (names, qname);
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
+ selector,
+ g_intern_string (name));
+
g_free (name);
- return TRUE;
+
+ return selector;
}
-static gboolean
+static GtkCssSelector *
parse_selector_pseudo_class (GtkCssParser *parser,
- GtkRegionFlags *region_to_modify,
- GtkStateFlags *state_to_modify)
+ GtkCssSelector *selector)
{
struct {
const char *name;
@@ -142,7 +536,9 @@ parse_selector_pseudo_class (GtkCssParser *parser,
if (name == NULL)
{
_gtk_css_parser_error (parser, "Missing name of pseudo-class");
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ return NULL;
}
if (_gtk_css_parser_try (parser, "(", TRUE))
@@ -153,7 +549,9 @@ parse_selector_pseudo_class (GtkCssParser *parser,
if (!_gtk_css_parser_try (parser, ")", FALSE))
{
_gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ return NULL;
}
if (g_ascii_strcasecmp (function, "nth-child") != 0)
@@ -164,7 +562,9 @@ parse_selector_pseudo_class (GtkCssParser *parser,
_gtk_css_parser_take_error (parser, error);
g_free (function);
g_free (name);
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ return NULL;
}
g_free (function);
@@ -175,7 +575,9 @@ parse_selector_pseudo_class (GtkCssParser *parser,
GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
"Unknown pseudo-class 'nth-child(%s)'", name);
_gtk_css_parser_take_error (parser, error);
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ return NULL;
}
classes = nth_child_classes;
@@ -187,11 +589,18 @@ parse_selector_pseudo_class (GtkCssParser *parser,
{
if (g_ascii_strcasecmp (name, classes[i].name) == 0)
{
- *region_to_modify |= classes[i].region_flag;
- *state_to_modify |= classes[i].state_flag;
-
g_free (name);
- return TRUE;
+
+ if (classes[i].region_flag)
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_REGION,
+ selector,
+ GUINT_TO_POINTER (classes[i].region_flag));
+ else
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
+ selector,
+ GUINT_TO_POINTER (classes[i].state_flag));
+
+ return selector;
}
}
@@ -204,58 +613,66 @@ parse_selector_pseudo_class (GtkCssParser *parser,
GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
"Unknown pseudo-class '%s'", name);
- g_free (name);
_gtk_css_parser_take_error (parser, error);
+ g_free (name);
+ if (selector)
+ _gtk_css_selector_free (selector);
- return FALSE;
+ return NULL;
}
-static gboolean
-parse_simple_selector (GtkCssParser *parser,
- char **name,
- GArray *ids,
- GArray *classes,
- GtkRegionFlags *pseudo_classes,
- GtkStateFlags *state)
-{
- gboolean parsed_something;
+static GtkCssSelector *
+try_parse_name (GtkCssParser *parser,
+ GtkCssSelector *selector)
+{
+ char *name;
+
+ name = _gtk_css_parser_try_ident (parser, FALSE);
+ if (name)
+ {
+ selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
+ ? >K_CSS_SELECTOR_REGION
+ : >K_CSS_SELECTOR_NAME,
+ selector,
+ g_intern_string (name));
+ g_free (name);
+ }
+ else if (_gtk_css_parser_try (parser, "*", FALSE))
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
- *name = _gtk_css_parser_try_ident (parser, FALSE);
- if (*name)
- parsed_something = TRUE;
- else
- parsed_something = _gtk_css_parser_try (parser, "*", FALSE);
+ return selector;
+}
+
+static GtkCssSelector *
+parse_simple_selector (GtkCssParser *parser,
+ GtkCssSelector *previous)
+{
+ GtkCssSelector *selector = previous;
+
+ selector = try_parse_name (parser, selector);
do {
if (_gtk_css_parser_try (parser, "#", FALSE))
- {
- if (!parse_selector_name (parser, ids))
- return FALSE;
- }
+ selector = parse_selector_id (parser, selector);
else if (_gtk_css_parser_try (parser, ".", FALSE))
- {
- if (!parse_selector_class (parser, classes))
- return FALSE;
- }
+ selector = parse_selector_class (parser, selector);
else if (_gtk_css_parser_try (parser, ":", FALSE))
- {
- if (!parse_selector_pseudo_class (parser, pseudo_classes, state))
- return FALSE;
- }
- else if (!parsed_something)
+ selector = parse_selector_pseudo_class (parser, selector);
+ else if (selector == previous)
{
_gtk_css_parser_error (parser, "Expected a valid selector");
- return FALSE;
+ if (selector)
+ _gtk_css_selector_free (selector);
+ selector = NULL;
}
else
break;
-
- parsed_something = TRUE;
}
- while (!_gtk_css_parser_is_eof (parser));
+ while (selector && !_gtk_css_parser_is_eof (parser));
_gtk_css_parser_skip_whitespace (parser);
- return TRUE;
+
+ return selector;
}
GtkCssSelector *
@@ -263,41 +680,16 @@ _gtk_css_selector_parse (GtkCssParser *parser)
{
GtkCssSelector *selector = NULL;
- do {
- char *name = NULL;
- GArray *ids = g_array_new (TRUE, FALSE, sizeof (GQuark));
- GArray *classes = g_array_new (TRUE, FALSE, sizeof (GQuark));
- GtkRegionFlags pseudo_classes = 0;
- GtkStateFlags state = 0;
- GtkCssCombinator combine = GTK_CSS_COMBINE_DESCANDANT;
-
- if (selector)
- {
- if (_gtk_css_parser_try (parser, ">", TRUE))
- combine = GTK_CSS_COMBINE_CHILD;
- }
-
- if (!parse_simple_selector (parser, &name, ids, classes, &pseudo_classes, &state))
- {
- g_array_free (ids, TRUE);
- g_array_free (classes, TRUE);
- if (selector)
- _gtk_css_selector_free (selector);
- return NULL;
- }
-
- selector = gtk_css_selector_new (selector,
- combine,
- name,
- (GQuark *) g_array_free (ids, ids->len == 0),
- (GQuark *) g_array_free (classes, classes->len == 0),
- pseudo_classes,
- state);
- g_free (name);
- }
- while (!_gtk_css_parser_is_eof (parser) &&
+ while ((selector = parse_simple_selector (parser, selector)) &&
+ !_gtk_css_parser_is_eof (parser) &&
!_gtk_css_parser_begins_with (parser, ',') &&
- !_gtk_css_parser_begins_with (parser, '{'));
+ !_gtk_css_parser_begins_with (parser, '{'))
+ {
+ if (_gtk_css_parser_try (parser, ">", TRUE))
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
+ else
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
+ }
return selector;
}
@@ -310,9 +702,6 @@ _gtk_css_selector_free (GtkCssSelector *selector)
if (selector->previous)
_gtk_css_selector_free (selector->previous);
- g_free (selector->ids);
- g_free (selector->classes);
-
g_slice_free (GtkCssSelector, selector);
}
@@ -320,96 +709,12 @@ void
_gtk_css_selector_print (const GtkCssSelector *selector,
GString * str)
{
- if (selector->previous)
- {
- _gtk_css_selector_print (selector->previous, str);
- switch (selector->combine)
- {
- case GTK_CSS_COMBINE_DESCANDANT:
- g_string_append (str, " ");
- break;
- case GTK_CSS_COMBINE_CHILD:
- g_string_append (str, " > ");
- break;
- default:
- g_assert_not_reached ();
- }
- }
-
- if (selector->name)
- g_string_append (str, selector->name);
- else if (selector->ids == NULL &&
- selector->classes == NULL &&
- selector->pseudo_classes == 0 &&
- selector->state == 0)
- g_string_append (str, "*");
-
- if (selector->ids)
- {
- GQuark *id;
-
- for (id = selector->ids; *id != 0; id++)
- {
- g_string_append_c (str, '#');
- g_string_append (str, g_quark_to_string (*id));
- }
- }
-
- if (selector->classes)
- {
- GQuark *class;
-
- for (class = selector->classes; *class != 0; class++)
- {
- g_string_append_c (str, '.');
- g_string_append (str, g_quark_to_string (*class));
- }
- }
+ g_return_if_fail (selector != NULL);
- if (selector->pseudo_classes)
- {
- static const char * flag_names[] = {
- "nth-child(even)",
- "nth-child(odd)",
- "first-child",
- "last-child",
- "only-child",
- "sorted"
- };
- guint i;
-
- for (i = 0; i < G_N_ELEMENTS (flag_names); i++)
- {
- if (selector->pseudo_classes & (1 << i))
- {
- g_string_append_c (str, ':');
- g_string_append (str, flag_names[i]);
- }
- }
- }
+ if (selector->previous)
+ _gtk_css_selector_print (selector->previous, str);
- if (selector->state)
- {
- static const char * state_names[] = {
- "active",
- "hover",
- "selected",
- "insensitive",
- "inconsistent",
- "focus",
- "backdrop"
- };
- guint i;
-
- for (i = 0; i < G_N_ELEMENTS (state_names); i++)
- {
- if (selector->state & (1 << i))
- {
- g_string_append_c (str, ':');
- g_string_append (str, state_names[i]);
- }
- }
- }
+ selector->class->print (selector, str);
}
char *
@@ -426,209 +731,6 @@ _gtk_css_selector_to_string (const GtkCssSelector *selector)
return g_string_free (string, FALSE);
}
-static GtkRegionFlags
-compute_region_flags_for_index (const GtkWidgetPath *path,
- guint id)
-{
- const GtkWidgetPath *siblings;
- guint sibling_id, n_siblings;
- GtkRegionFlags flags;
-
- siblings = gtk_widget_path_iter_get_siblings (path, id);
- if (siblings == NULL)
- return 0;
-
- sibling_id = gtk_widget_path_iter_get_sibling_index (path, id);
- n_siblings = gtk_widget_path_length (siblings);
-
- flags = (sibling_id % 2) ? GTK_REGION_EVEN : GTK_REGION_ODD;
- if (sibling_id == 0)
- flags |= GTK_REGION_FIRST;
- if (sibling_id + 1 == n_siblings)
- flags |= GTK_REGION_LAST;
- if (n_siblings == 1)
- flags |= GTK_REGION_ONLY;
-
- return flags;
-}
-
-static gboolean
-gtk_css_selector_matches_type (const GtkCssSelector *selector,
- const GtkWidgetPath *path,
- guint id)
-{
- if (selector->pseudo_classes)
- {
- GtkRegionFlags flags = compute_region_flags_for_index (path, id);
-
- if ((selector->pseudo_classes & flags) != selector->pseudo_classes)
- return FALSE;
- }
-
- if (selector->name == NULL)
- return TRUE;
-
- if (selector->type == G_TYPE_NONE)
- return FALSE;
-
- /* ugh, assigning to a const variable */
- if (selector->type == G_TYPE_INVALID)
- ((GtkCssSelector *) selector)->type = g_type_from_name (selector->name);
-
- if (selector->type == G_TYPE_INVALID)
- return FALSE;
-
- return g_type_is_a (gtk_widget_path_iter_get_object_type (path, id), selector->type);
-}
-
-static gboolean
-gtk_css_selector_matches_region (const GtkCssSelector *selector,
- const GtkWidgetPath *path,
- guint id,
- const char * region)
-{
- GtkRegionFlags flags;
-
- if (selector->name == NULL)
- return TRUE;
-
- if (selector->name != region)
- return FALSE;
-
- if (!gtk_widget_path_iter_has_region (path, id, region, &flags))
- {
- /* This function must be called with existing regions */
- g_assert_not_reached ();
- }
-
- return (selector->pseudo_classes & flags) == selector->pseudo_classes;
-}
-
-static gboolean
-gtk_css_selector_matches_rest (const GtkCssSelector *selector,
- const GtkWidgetPath *path,
- guint id)
-{
- if (selector->ids)
- {
- GQuark *name;
-
- for (name = selector->ids; *name; name++)
- {
- if (!gtk_widget_path_iter_has_qname (path, id, *name))
- return FALSE;
- }
- }
-
- if (selector->classes)
- {
- GQuark *class;
-
- for (class = selector->classes; *class; class++)
- {
- if (!gtk_widget_path_iter_has_qclass (path, id, *class))
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-gtk_css_selector_matches_previous (const GtkCssSelector *selector,
- const GtkWidgetPath *path,
- guint id,
- GSList *regions);
-
-static gboolean
-gtk_css_selector_matches_from (const GtkCssSelector *selector,
- const GtkWidgetPath *path,
- guint id,
- GSList *regions)
-{
- GSList *l;
-
- if (!gtk_css_selector_matches_rest (selector, path, id))
- return FALSE;
-
- for (l = regions; l; l = l->next)
- {
- const char *region = l->data;
-
- if (gtk_css_selector_matches_region (selector, path, id, region))
- {
- GSList *remaining;
- gboolean match;
-
- remaining = g_slist_copy (regions);
- remaining = g_slist_remove (remaining, region);
- match = gtk_css_selector_matches_previous (selector,
- path,
- id,
- remaining);
- g_slist_free (remaining);
- if (match)
- return TRUE;
- }
- }
-
- if (gtk_css_selector_matches_type (selector, path, id))
- {
- GSList *regions;
- gboolean match;
-
- if (id <= 0)
- return selector->previous == NULL;
-
- regions = gtk_widget_path_iter_list_regions (path, id - 1);
- match = gtk_css_selector_matches_previous (selector,
- path,
- id - 1,
- regions);
- g_slist_free (regions);
- return match;
- }
-
- return FALSE;
-}
-
-static gboolean
-gtk_css_selector_matches_previous (const GtkCssSelector *selector,
- const GtkWidgetPath *path,
- guint id,
- GSList *regions)
-{
- if (!selector->previous)
- return TRUE;
-
- if (gtk_css_selector_matches_from (selector->previous,
- path,
- id,
- regions))
- return TRUE;
-
- if (selector->combine == GTK_CSS_COMBINE_DESCANDANT)
- {
- /* with this magic we run the loop while id >= 0 */
- while (id-- != 0)
- {
- GSList *list;
- gboolean match;
-
- list = gtk_widget_path_iter_list_regions (path, id);
- match = gtk_css_selector_matches_from (selector->previous,
- path,
- id,
- list);
- g_slist_free (list);
- if (match)
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
/**
* _gtk_css_selector_matches:
* @selector: the selector
@@ -648,36 +750,19 @@ _gtk_css_selector_matches (const GtkCssSelector *selector,
const GtkWidgetPath *path,
GtkStateFlags state)
{
- GSList *list;
- gboolean match;
guint length;
g_return_val_if_fail (selector != NULL, FALSE);
g_return_val_if_fail (path != NULL, FALSE);
- if ((selector->state & state) != selector->state)
- return FALSE;
-
length = gtk_widget_path_length (path);
if (length == 0)
return FALSE;
- list = gtk_widget_path_iter_list_regions (path, length - 1);
- match = gtk_css_selector_matches_from (selector,
- path,
- length - 1,
- list);
- g_slist_free (list);
- return match;
-}
-
-static guint
-count_bits (guint v)
-{
- /* http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel */
- v = v - ((v >> 1) & 0x55555555);
- v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
- return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
+ return gtk_css_selector_match (selector,
+ state,
+ path,
+ length - 1);
}
/* Computes specificity according to CSS 2.1.
@@ -688,23 +773,17 @@ _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
guint *classes,
guint *elements)
{
- GQuark *count;
-
- if (selector->previous)
- _gtk_css_selector_get_specificity (selector->previous, ids, classes, elements);
-
- if (selector->ids)
- for (count = selector->ids; *count; count++)
- (*ids)++;
-
- if (selector->classes)
- for (count = selector->classes; *count; count++)
- (*classes)++;
-
- *classes += count_bits (selector->state) + count_bits (selector->pseudo_classes);
-
- if (selector->name)
- (*elements)++;
+ for (; selector; selector = selector->previous)
+ {
+ const GtkCssSelectorClass *klass = selector->class;
+
+ if (klass->increase_id_specificity)
+ (*ids)++;
+ if (klass->increase_class_specificity)
+ (*classes)++;
+ if (klass->increase_element_specificity)
+ (*elements)++;
+ }
}
int
@@ -732,8 +811,13 @@ _gtk_css_selector_compare (const GtkCssSelector *a,
GtkStateFlags
_gtk_css_selector_get_state_flags (GtkCssSelector *selector)
{
+ GtkStateFlags state = 0;
+
g_return_val_if_fail (selector != NULL, 0);
- return selector->state;
+ for (; selector && selector->class == >K_CSS_SELECTOR_NAME; selector = selector->previous)
+ state |= GPOINTER_TO_UINT (selector->data);
+
+ return state;
}
diff --git a/tests/css/parser/selector.ref.css b/tests/css/parser/selector.ref.css
index e982145..5be0d41 100644
--- a/tests/css/parser/selector.ref.css
+++ b/tests/css/parser/selector.ref.css
@@ -38,7 +38,7 @@ a > b {
int-property: 42;
}
-.b {
+*.b {
int-property: 42;
}
@@ -50,7 +50,7 @@ a > b {
int-property: 42;
}
-:hover {
+*:hover {
int-property: 42;
}
@@ -150,7 +150,7 @@ a > :hover {
int-property: 42;
}
-.b:hover {
+:hover.b {
int-property: 42;
}
@@ -174,7 +174,7 @@ a > :hover {
int-property: 42;
}
-#b {
+*#b {
int-property: 42;
}
@@ -218,7 +218,7 @@ a > #b {
int-property: 42;
}
-#b.a {
+.a#b {
int-property: 42;
}
@@ -230,7 +230,7 @@ a > #b {
int-property: 42;
}
-#b:hover {
+:hover#b {
int-property: 42;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]