diff --git a/gtk/gtkcssselector.c b/gtk/gtkcssselector.c index 96f010adbf..20aec4518b 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) +static GtkCssSelector * +try_parse_name (GtkCssParser *parser, + GtkCssSelector *selector) { - gboolean parsed_something; + 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) { + g_return_if_fail (selector != NULL); + 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 (); - } - } + _gtk_css_selector_print (selector->previous, str); - 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)); - } - } - - 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->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; + for (; selector; selector = selector->previous) + { + const GtkCssSelectorClass *klass = selector->class; - 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)++; + 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 e982145719..5be0d413d9 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; }