#include "gtkcssprovider.h"
#include "gtkstylecontextprivate.h"
+/* When checking for changes via the tree we need to know if a rule further
+ down the tree matched, because if so we need to add "our bit" to the
+ Change. For instance in a a match like *.class:active we'll
+ get a tree that first checks :active, if that matches we continue down
+ to the tree, and if we get a match we add CHANGE_CLASS. However, the
+ end of the tree where we have a match is an ANY which doesn't actually
+ modify the change, so we don't know if we have a match or not. We fix
+ this by setting GTK_CSS_CHANGE_GOT_MATCH which lets us guarantee
+ that change != 0 on any match. */
+#define GTK_CSS_CHANGE_GOT_MATCH GTK_CSS_CHANGE_RESERVED_BIT
+
typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
struct _GtkCssSelectorClass {
void (* tree_match) (const GtkCssSelectorTree *tree,
const GtkCssMatcher *matcher,
GHashTable *res);
- GtkCssChange (* get_change) (const GtkCssSelector *selector);
+ GtkCssChange (* get_change) (const GtkCssSelector *selector,
+ GtkCssChange previous_change);
+ GtkCssChange (* tree_get_change) (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher);
+ int (* compare_one) (const GtkCssSelector *a,
+ const GtkCssSelector *b);
guint increase_id_specificity :1;
guint increase_class_specificity :1;
- GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
};
+#define GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET G_MAXINT32
struct _GtkCssSelectorTree
{
GtkCssSelector selector;
- GtkCssSelectorTree *parent;
- GtkCssSelectorTree *previous;
- GtkCssSelectorTree *siblings;
- gpointer *matches; /* pointers that we return as matches if selector matches */
+ gint32 parent_offset;
+ gint32 previous_offset;
+ gint32 sibling_offset;
+ gint32 matches_offset; /* pointers that we return as matches if selector matches */
};
static gboolean
return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
}
+static gpointer *
+gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
+{
+ if (tree->matches_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ return NULL;
+
+ return (gpointer *) ((guint8 *)tree + tree->matches_offset);
+}
+
static void
gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
GHashTable *res)
{
int i;
+ gpointer *matches;
- if (tree->matches)
+ matches = gtk_css_selector_tree_get_matches (tree);
+ if (matches)
{
- for (i = 0; tree->matches[i] != NULL; i++)
- g_hash_table_insert (res, tree->matches[i], tree->matches[i]);
+ for (i = 0; matches[i] != NULL; i++)
+ g_hash_table_insert (res, matches[i], matches[i]);
}
}
tree->selector.class->tree_match (tree, matcher, res);
}
+static GtkCssChange
+gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ if (tree == NULL)
+ return 0;
+
+ return tree->selector.class->tree_get_change (tree, matcher);
+}
+
static gboolean
gtk_css_selector_match (const GtkCssSelector *selector,
const GtkCssMatcher *matcher)
return selector->class->match (selector, matcher);
}
-static GtkCssChange
-gtk_css_selector_get_change (const GtkCssSelector *selector)
+static int
+gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
{
- if (selector == NULL)
- return 0;
-
- return selector->class->get_change (selector);
+ if (a->class != b->class)
+ return strcmp (a->class->name, b->class->name);
+ else
+ return a->class->compare_one (a, b);
}
-
+
static const GtkCssSelector *
gtk_css_selector_previous (const GtkCssSelector *selector)
{
return selector->class ? selector : NULL;
}
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_at_offset (const GtkCssSelectorTree *tree,
+ gint32 offset)
+{
+ if (offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ return NULL;
+
+ return (GtkCssSelectorTree *) ((guint8 *)tree + offset);
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_get_parent (const GtkCssSelectorTree *tree)
+{
+ return gtk_css_selector_tree_at_offset (tree, tree->parent_offset);
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_get_previous (const GtkCssSelectorTree *tree)
+{
+ return gtk_css_selector_tree_at_offset (tree, tree->previous_offset);
+}
+
+static const GtkCssSelectorTree *
+gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
+{
+ return gtk_css_selector_tree_at_offset (tree, tree->sibling_offset);
+}
+
+static void
+gtk_css_selector_tree_match_previous (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher,
+ GHashTable *res)
+{
+ const GtkCssSelectorTree *prev;
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ gtk_css_selector_tree_match (prev, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_tree_get_previous_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange previous_change = 0;
+ const GtkCssSelectorTree *prev;
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+
+ return previous_change;
+}
+
/* DESCENDANT */
static void
GHashTable *res)
{
GtkCssMatcher ancestor;
- const GtkCssSelectorTree *prev;
while (_gtk_css_matcher_get_parent (&ancestor, matcher))
{
matcher = &ancestor;
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+
+ /* any matchers are dangerous here, as we may loop forever, but
+ we can terminate now as all possible matches have already been added */
+ if (_gtk_css_matcher_matches_any (matcher))
+ break;
+ }
+}
+
+static GtkCssChange
+gtk_css_selector_descendant_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher ancestor;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+ previous_change = 0;
+ while (_gtk_css_matcher_get_parent (&ancestor, matcher))
+ {
+ matcher = &ancestor;
+
+ previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
/* any matchers are dangerous here, as we may loop forever, but
we can terminate now as all possible matches have already been added */
if (_gtk_css_matcher_matches_any (matcher))
break;
}
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
+static int
+gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
static GtkCssChange
-gtk_css_selector_descendant_get_change (const GtkCssSelector *selector)
+gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
+ return _gtk_css_change_for_child (previous_change);
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
gtk_css_selector_descendant_match,
gtk_css_selector_descendant_tree_match,
gtk_css_selector_descendant_get_change,
+ gtk_css_selector_descendant_tree_get_change,
+ gtk_css_selector_descendant_compare_one,
FALSE, FALSE, FALSE, FALSE, FALSE
};
GHashTable *res)
{
GtkCssMatcher parent;
- const GtkCssSelectorTree *prev;
if (!_gtk_css_matcher_get_parent (&parent, matcher))
return;
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, &parent, res);
+ gtk_css_selector_tree_match_previous (tree, &parent, res);
+}
+
+
+static GtkCssChange
+gtk_css_selector_child_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher parent;
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_get_parent (&parent, matcher))
+ return 0;
+
+ change = 0;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, &parent);
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
static GtkCssChange
-gtk_css_selector_child_get_change (const GtkCssSelector *selector)
+gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
+ return _gtk_css_change_for_child (previous_change);
+}
+
+static int
+gtk_css_selector_child_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
gtk_css_selector_child_match,
gtk_css_selector_child_tree_match,
gtk_css_selector_child_get_change,
+ gtk_css_selector_child_tree_get_change,
+ gtk_css_selector_child_compare_one,
FALSE, FALSE, FALSE, FALSE, FALSE
};
GHashTable *res)
{
GtkCssMatcher previous;
- const GtkCssSelectorTree *prev;
while (_gtk_css_matcher_get_previous (&previous, matcher))
{
matcher = &previous;
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+
+ /* any matchers are dangerous here, as we may loop forever, but
+ we can terminate now as all possible matches have already been added */
+ if (_gtk_css_matcher_matches_any (matcher))
+ break;
+ }
+}
+
+static GtkCssChange
+gtk_css_selector_sibling_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher previous;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+
+ previous_change = 0;
+ while (_gtk_css_matcher_get_previous (&previous, matcher))
+ {
+ matcher = &previous;
+
+ previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
/* any matchers are dangerous here, as we may loop forever, but
we can terminate now as all possible matches have already been added */
if (_gtk_css_matcher_matches_any (matcher))
break;
}
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
static GtkCssChange
-gtk_css_selector_sibling_get_change (const GtkCssSelector *selector)
+gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
+ return _gtk_css_change_for_sibling (previous_change);
}
+static int
+gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
+}
+
+
static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
"sibling",
gtk_css_selector_sibling_print,
gtk_css_selector_sibling_match,
gtk_css_selector_sibling_tree_match,
gtk_css_selector_sibling_get_change,
+ gtk_css_selector_sibling_tree_get_change,
+ gtk_css_selector_sibling_compare_one,
FALSE, FALSE, FALSE, FALSE, FALSE
};
GHashTable *res)
{
GtkCssMatcher previous;
- const GtkCssSelectorTree *prev;
if (!_gtk_css_matcher_get_previous (&previous, matcher))
return;
matcher = &previous;
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_adjacent_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssMatcher previous;
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_get_previous (&previous, matcher))
+ return 0;
+
+ change = 0;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, &previous);
+
+ if (previous_change != 0)
+ change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
static GtkCssChange
-gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector)
+gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
+{
+ return _gtk_css_change_for_sibling (previous_change);
+}
+
+static int
+gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
{
- return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
+ return 0;
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
gtk_css_selector_adjacent_match,
gtk_css_selector_adjacent_tree_match,
gtk_css_selector_adjacent_get_change,
+ gtk_css_selector_adjacent_tree_get_change,
+ gtk_css_selector_adjacent_compare_one,
FALSE, FALSE, FALSE, FALSE, FALSE
};
const GtkCssMatcher *matcher,
GHashTable *res)
{
- const GtkCssSelectorTree *prev, *prev2;
+ const GtkCssSelectorTree *prev;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
{
if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
_gtk_css_matcher_has_regions (matcher))
- {
- for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
- gtk_css_selector_tree_match (prev2, matcher, res);
- }
+ gtk_css_selector_tree_match_previous (prev, matcher, res);
gtk_css_selector_tree_match (prev, matcher, res);
}
}
static GtkCssChange
-gtk_css_selector_any_get_change (const GtkCssSelector *selector)
+gtk_css_selector_any_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelectorTree *prev;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = 0;
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
+ _gtk_css_matcher_has_regions (matcher))
+ previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
+
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+ }
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+
+static GtkCssChange
+gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return gtk_css_selector_get_change (gtk_css_selector_previous (selector));
+ return previous_change;
+}
+
+static int
+gtk_css_selector_any_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return 0;
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
gtk_css_selector_any_match,
gtk_css_selector_any_tree_match,
gtk_css_selector_any_get_change,
+ gtk_css_selector_any_tree_get_change,
+ gtk_css_selector_any_compare_one,
FALSE, FALSE, FALSE, TRUE, TRUE
};
/* NAME */
+typedef struct {
+ GType type;
+ const char *name;
+} TypeReference;
+
+static GHashTable *type_refs_ht = NULL;
+static guint type_refs_last_serial = 0;
+
+static TypeReference *
+get_type_reference (const char *name)
+{
+ TypeReference *ref;
+
+
+ if (type_refs_ht == NULL)
+ type_refs_ht = g_hash_table_new (g_str_hash, g_str_equal);
+
+ ref = g_hash_table_lookup (type_refs_ht, name);
+
+ if (ref != NULL)
+ return ref;
+
+ ref = g_slice_new (TypeReference);
+ ref->name = g_intern_string (name);
+ ref->type = g_type_from_name (ref->name);
+
+ g_hash_table_insert (type_refs_ht,
+ (gpointer)ref->name, ref);
+
+ return ref;
+}
+
+static void
+update_type_references (void)
+{
+ GHashTableIter iter;
+ guint serial;
+ gpointer value;
+
+ serial = g_type_get_type_registration_serial ();
+
+ if (serial == type_refs_last_serial)
+ return;
+
+ type_refs_last_serial = serial;
+
+ if (type_refs_ht == NULL)
+ return;
+
+ g_hash_table_iter_init (&iter, type_refs_ht);
+ while (g_hash_table_iter_next (&iter,
+ NULL, &value))
+ {
+ TypeReference *ref = value;
+ if (ref->type == G_TYPE_INVALID)
+ ref->type = g_type_from_name (ref->name);
+ }
+}
+
static void
gtk_css_selector_name_print (const GtkCssSelector *selector,
GString *string)
{
- g_string_append (string, selector->data);
+ g_string_append (string, ((TypeReference *)selector->data)->name);
}
static gboolean
gtk_css_selector_name_match (const GtkCssSelector *selector,
const GtkCssMatcher *matcher)
{
- if (!_gtk_css_matcher_has_name (matcher, selector->data))
+ if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)selector->data)->type))
return FALSE;
return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
const GtkCssMatcher *matcher,
GHashTable *res)
{
- const GtkCssSelectorTree *prev;
-
- if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
+ if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)tree->selector.data)->type))
return;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_name_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)tree->selector.data)->type))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ if (previous_change)
+ change |= previous_change | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
static GtkCssChange
-gtk_css_selector_name_get_change (const GtkCssSelector *selector)
+gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_NAME;
+ return previous_change | GTK_CSS_CHANGE_NAME;
+}
+
+static int
+gtk_css_selector_name_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return strcmp (((TypeReference *)a->data)->name,
+ ((TypeReference *)b->data)->name);
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
gtk_css_selector_name_match,
gtk_css_selector_name_tree_match,
gtk_css_selector_name_get_change,
+ gtk_css_selector_name_tree_get_change,
+ gtk_css_selector_name_compare_one,
FALSE, FALSE, TRUE, TRUE, FALSE
};
const GtkCssMatcher *matcher,
GHashTable *res)
{
- const GtkCssSelectorTree *prev, *prev2;
+ const GtkCssSelectorTree *prev;
if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
return;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
{
if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
- {
- for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
- gtk_css_selector_tree_match (prev2, matcher, res);
- }
+ gtk_css_selector_tree_match_previous (prev, matcher, res);
gtk_css_selector_tree_match (prev, matcher, res);
}
}
static GtkCssChange
-gtk_css_selector_region_get_change (const GtkCssSelector *selector)
+gtk_css_selector_region_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelectorTree *prev;
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_REGION | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = 0;
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
+ previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
+
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+ }
+
+ if (previous_change != 0)
+ {
+ previous_change |= GTK_CSS_CHANGE_REGION;
+ previous_change |= _gtk_css_change_for_child (previous_change);
+ change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
+ }
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
GtkCssChange change;
- change = gtk_css_selector_get_change (gtk_css_selector_previous (selector));
+ change = previous_change;
change |= GTK_CSS_CHANGE_REGION;
change |= _gtk_css_change_for_child (change);
return change;
}
+static int
+gtk_css_selector_region_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return strcmp (a->data, b->data);
+}
+
static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
"region",
gtk_css_selector_region_print,
gtk_css_selector_region_match,
gtk_css_selector_region_tree_match,
gtk_css_selector_region_get_change,
+ gtk_css_selector_region_tree_get_change,
+ gtk_css_selector_region_compare_one,
FALSE, FALSE, TRUE, TRUE, TRUE
};
const GtkCssMatcher *matcher,
GHashTable *res)
{
- const GtkCssSelectorTree *prev;
-
if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
return;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_class_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
static GtkCssChange
-gtk_css_selector_class_get_change (const GtkCssSelector *selector)
+gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_CLASS;
+ return previous_change | GTK_CSS_CHANGE_CLASS;
+}
+
+static int
+gtk_css_selector_class_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
+ g_quark_to_string (GPOINTER_TO_UINT (b->data)));
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
gtk_css_selector_class_match,
gtk_css_selector_class_tree_match,
gtk_css_selector_class_get_change,
+ gtk_css_selector_class_tree_get_change,
+ gtk_css_selector_class_compare_one,
FALSE, TRUE, FALSE, TRUE, FALSE
};
const GtkCssMatcher *matcher,
GHashTable *res)
{
- const GtkCssSelectorTree *prev;
-
if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
return;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
+}
+
+static GtkCssChange
+gtk_css_selector_id_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange change, previous_change;
+
+ if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
}
static GtkCssChange
-gtk_css_selector_id_get_change (const GtkCssSelector *selector)
+gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_ID;
+ return previous_change | GTK_CSS_CHANGE_ID;
+}
+
+
+static int
+gtk_css_selector_id_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return strcmp (a->data, b->data);
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
gtk_css_selector_id_match,
gtk_css_selector_id_tree_match,
gtk_css_selector_id_get_change,
+ gtk_css_selector_id_tree_get_change,
+ gtk_css_selector_id_compare_one,
TRUE, FALSE, FALSE, TRUE, FALSE
};
"insensitive",
"inconsistent",
"focus",
- "backdrop"
+ "backdrop",
+ "dir(ltr)",
+ "dir(rtl)"
};
guint i, state;
GHashTable *res)
{
GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
- const GtkCssSelectorTree *prev;
if ((_gtk_css_matcher_get_state (matcher) & state) != state)
return;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
- gtk_css_selector_tree_match (prev, matcher, res);
+ gtk_css_selector_tree_match_previous (tree, matcher, res);
}
+static GtkCssChange
+gtk_css_selector_pseudoclass_state_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
+ GtkCssChange change, previous_change;
+
+ if ((_gtk_css_matcher_get_state (matcher) & state) != state)
+ return 0;
+
+ change = 0;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
static GtkCssChange
-gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector)
+gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_STATE;
+ return previous_change | GTK_CSS_CHANGE_STATE;
+}
+
+static int
+gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
gtk_css_selector_pseudoclass_state_match,
gtk_css_selector_pseudoclass_state_tree_match,
gtk_css_selector_pseudoclass_state_get_change,
+ gtk_css_selector_pseudoclass_state_tree_get_change,
+ gtk_css_selector_pseudoclass_state_compare_one,
FALSE, TRUE, FALSE, TRUE, FALSE
};
}
static gboolean
-gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
- const GtkCssMatcher *matcher)
+get_selector_flags_for_position_region_match (const GtkCssSelector *selector, GtkRegionFlags *selector_flags)
{
- GtkRegionFlags selector_flags;
- const GtkCssSelector *previous;
PositionType type;
int a, b;
{
case POSITION_FORWARD:
if (a == 0 && b == 1)
- selector_flags = GTK_REGION_FIRST;
+ *selector_flags = GTK_REGION_FIRST;
else if (a == 2 && b == 0)
- selector_flags = GTK_REGION_EVEN;
+ *selector_flags = GTK_REGION_EVEN;
else if (a == 2 && b == 1)
- selector_flags = GTK_REGION_ODD;
+ *selector_flags = GTK_REGION_ODD;
else
return FALSE;
break;
case POSITION_BACKWARD:
if (a == 0 && b == 1)
- selector_flags = GTK_REGION_LAST;
+ *selector_flags = GTK_REGION_LAST;
else
return FALSE;
break;
case POSITION_ONLY:
- selector_flags = GTK_REGION_ONLY;
+ *selector_flags = GTK_REGION_ONLY;
break;
case POSITION_SORTED:
- selector_flags = GTK_REGION_SORTED;
+ *selector_flags = GTK_REGION_SORTED;
break;
default:
g_assert_not_reached ();
break;
}
+
+ return TRUE;
+}
+
+static gboolean
+gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ GtkRegionFlags selector_flags;
+ const GtkCssSelector *previous;
+
+ if (!get_selector_flags_for_position_region_match (selector, &selector_flags))
+ return FALSE;
+
selector = gtk_css_selector_previous (selector);
if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
}
static gboolean
-gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
- const GtkCssMatcher *matcher)
+get_position_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
{
- const GtkCssSelector *previous;
PositionType type;
int a, b;
- previous = gtk_css_selector_previous (selector);
- if (previous && previous->class == >K_CSS_SELECTOR_REGION)
- return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
-
decode_position (selector, &type, &a, &b);
switch (type)
{
return FALSE;
}
+ return TRUE;
+}
+
+static gboolean
+gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelector *previous;
+
+ previous = gtk_css_selector_previous (selector);
+ if (previous && previous->class == >K_CSS_SELECTOR_REGION)
+ return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
+
+ if (!get_position_match (selector, matcher))
+ return FALSE;
+
return gtk_css_selector_match (previous, matcher);
}
{
const GtkCssSelectorTree *prev2;
GtkRegionFlags selector_flags;
- PositionType type;
- int a, b;
- decode_position (&tree->selector, &type, &a, &b);
- switch (type)
- {
- case POSITION_FORWARD:
- if (a == 0 && b == 1)
- selector_flags = GTK_REGION_FIRST;
- else if (a == 2 && b == 0)
- selector_flags = GTK_REGION_EVEN;
- else if (a == 2 && b == 1)
- selector_flags = GTK_REGION_ODD;
- else
- return;
- break;
- case POSITION_BACKWARD:
- if (a == 0 && b == 1)
- selector_flags = GTK_REGION_LAST;
- else
- return;
- break;
- case POSITION_ONLY:
- selector_flags = GTK_REGION_ONLY;
- break;
- case POSITION_SORTED:
- selector_flags = GTK_REGION_SORTED;
- break;
- default:
- g_assert_not_reached ();
- break;
- }
+ if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
+ return;
if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
return;
gtk_css_selector_tree_found_match (prev, res);
- for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
+ for (prev2 = gtk_css_selector_tree_get_previous (prev);
+ prev2 != NULL;
+ prev2 = gtk_css_selector_tree_get_sibling (prev2))
{
if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
- gtk_css_selector_tree_match (prev2->previous, matcher, res);
+ gtk_css_selector_tree_match (gtk_css_selector_tree_get_previous (prev2), matcher, res);
gtk_css_selector_tree_match (prev2, matcher, res);
}
}
GHashTable *res)
{
const GtkCssSelectorTree *prev;
- PositionType type;
- int a, b;
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
{
if (prev->selector.class == >K_CSS_SELECTOR_REGION)
gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
}
- decode_position (&tree->selector, &type, &a, &b);
- switch (type)
- {
- case POSITION_FORWARD:
- if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
- return;
- break;
- case POSITION_BACKWARD:
- if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
- return;
- break;
- case POSITION_ONLY:
- if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
- !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
- return;
- break;
- case POSITION_SORTED:
- return;
- default:
- g_assert_not_reached ();
- return;
- }
+ if (!get_position_match (&tree->selector, matcher))
+ return;
gtk_css_selector_tree_found_match (tree, res);
- for (prev = tree->previous; prev != NULL; prev = prev->siblings)
+ for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
{
if (prev->selector.class != >K_CSS_SELECTOR_REGION)
gtk_css_selector_tree_match (prev, matcher, res);
}
static GtkCssChange
-gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector)
+gtk_css_selector_pseudoclass_position_tree_get_change_for_region (const GtkCssSelectorTree *tree,
+ const GtkCssSelectorTree *prev,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelectorTree *prev2;
+ GtkRegionFlags selector_flags;
+ GtkCssChange change, previous_change;
+
+ if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
+ return 0;
+
+ if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
+ return 0;
+
+ change = 0;
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = 0;
+ for (prev2 = gtk_css_selector_tree_get_previous (prev);
+ prev2 != NULL;
+ prev2 = gtk_css_selector_tree_get_sibling (prev2))
+ {
+ if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
+ previous_change |= gtk_css_selector_tree_get_change (gtk_css_selector_tree_get_previous (prev2), matcher);
+ previous_change |= gtk_css_selector_tree_get_change (prev2, matcher);
+ }
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_pseudoclass_position_tree_get_change (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ const GtkCssSelectorTree *prev;
+ GtkCssChange change, previous_change;
+
+ change = 0;
+
+ for (prev = gtk_css_selector_tree_get_previous (tree);
+ prev != NULL;
+ prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class == >K_CSS_SELECTOR_REGION)
+ change |= gtk_css_selector_pseudoclass_position_tree_get_change_for_region (tree, prev, matcher);
+ }
+
+ if (!get_position_match (&tree->selector, matcher))
+ return change;
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+ previous_change = 0;
+ for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
+ {
+ if (prev->selector.class != >K_CSS_SELECTOR_REGION)
+ previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
+ }
+
+ if (previous_change != 0)
+ change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
+
+ return change;
+}
+
+static GtkCssChange
+gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
{
- return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_POSITION;
+ return previous_change | GTK_CSS_CHANGE_POSITION;
+}
+
+static int
+gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
+ const GtkCssSelector *b)
+{
+ return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
}
static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
gtk_css_selector_pseudoclass_position_match,
gtk_css_selector_pseudoclass_position_tree_match,
gtk_css_selector_pseudoclass_position_get_change,
+ gtk_css_selector_pseudoclass_position_tree_get_change,
+ gtk_css_selector_pseudoclass_position_compare_one,
FALSE, TRUE, FALSE, TRUE, TRUE
};
{ "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
{ "focused", GTK_STATE_FLAG_FOCUSED, },
{ "focus", GTK_STATE_FLAG_FOCUSED, },
- { "backdrop", GTK_STATE_FLAG_BACKDROP, }
+ { "backdrop", GTK_STATE_FLAG_BACKDROP, },
+ { "dir(ltr)", GTK_STATE_FLAG_DIR_LTR, },
+ { "dir(rtl)", GTK_STATE_FLAG_DIR_RTL, }
};
guint i;
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));
+ if (_gtk_style_context_check_region_name (name))
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_REGION,
+ selector,
+ g_intern_string (name));
+ else
+ selector = gtk_css_selector_new (>K_CSS_SELECTOR_NAME,
+ selector,
+ get_type_reference (name));
g_free (name);
}
else if (_gtk_css_parser_try (parser, "*", FALSE))
return g_string_free (string, FALSE);
}
+
GtkCssChange
-_gtk_css_selector_get_change (const GtkCssSelector *selector)
+_gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree)
{
- g_return_val_if_fail (selector != NULL, 0);
+ GtkCssChange change = 0;
+
+ update_type_references ();
- return gtk_css_selector_get_change (selector);
+ while (tree)
+ {
+ change = tree->selector.class->get_change (&tree->selector, change);
+ tree = gtk_css_selector_tree_get_parent (tree);
+ }
+
+ return change;
}
/**
g_return_val_if_fail (selector != NULL, FALSE);
g_return_val_if_fail (matcher != NULL, FALSE);
+ update_type_references ();
+
return gtk_css_selector_match (selector, matcher);
}
}
GPtrArray *
-_gtk_css_selector_tree_match_all (GtkCssSelectorTree *tree,
+_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
const GtkCssMatcher *matcher)
{
GHashTable *res;
GHashTableIter iter;
gpointer key;
+ update_type_references ();
+
res = g_hash_table_new (g_direct_hash, g_direct_equal);
- for (; tree != NULL; tree = tree->siblings)
+ for (; tree != NULL;
+ tree = gtk_css_selector_tree_get_sibling (tree))
gtk_css_selector_tree_match (tree, matcher, res);
array = g_ptr_array_sized_new (g_hash_table_size (res));
return array;
}
+GtkCssChange
+_gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
+ const GtkCssMatcher *matcher)
+{
+ GtkCssChange change;
+
+ change = 0;
+
+ for (; tree != NULL;
+ tree = gtk_css_selector_tree_get_sibling (tree))
+ change |= gtk_css_selector_tree_get_change (tree, matcher);
+
+ /* Never return reserved bit set */
+ return change & ~GTK_CSS_CHANGE_RESERVED_BIT;
+}
+
#ifdef PRINT_TREE
static void
_gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
tree->selector.class->print (&tree->selector, str);
len = str->len - len;
- if (tree->previous)
+ if (gtk_css_selector_tree_get_previous (tree))
{
GString *prefix2 = g_string_new (prefix);
for (i = 0; i < len; i++)
g_string_append_c (prefix2, ' ');
- _gtk_css_selector_tree_print (tree->previous, str, prefix2->str);
+ _gtk_css_selector_tree_print (gtk_css_selector_tree_get_previous (tree), str, prefix2->str);
g_string_free (prefix2, TRUE);
}
else
}
#endif
+void
+_gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
+ GString *str)
+{
+ const GtkCssSelectorTree *parent;
+
+ g_return_if_fail (tree != NULL);
+
+ tree->selector.class->print (&tree->selector, str);
+
+ parent = gtk_css_selector_tree_get_parent (tree);
+ if (parent != NULL)
+ _gtk_css_selector_tree_match_print (parent, str);
+}
+
void
_gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
{
if (tree == NULL)
return;
- _gtk_css_selector_tree_free (tree->siblings);
- _gtk_css_selector_tree_free (tree->previous);
-
g_free (tree);
}
typedef struct {
gpointer match;
GtkCssSelector *current_selector;
+ GtkCssSelectorTree **selector_match;
} GtkCssSelectorRuleSetInfo;
+static GtkCssSelectorTree *
+get_tree (GByteArray *array, gint32 offset)
+{
+ return (GtkCssSelectorTree *) (array->data + offset);
+}
static GtkCssSelectorTree *
-subdivide_infos (GList *infos, GtkCssSelectorTree *parent)
+alloc_tree (GByteArray *array, gint32 *offset)
{
- GHashTable *ht = gtk_css_selectors_count_initial_init ();
+ GtkCssSelectorTree tree = { { NULL} };
+
+ *offset = array->len;
+ g_byte_array_append (array, (guint8 *)&tree, sizeof (GtkCssSelectorTree));
+ return get_tree (array, *offset);
+}
+
+static gint32
+subdivide_infos (GByteArray *array, GList *infos, gint32 parent_offset)
+{
+ GHashTable *ht;
GList *l;
GList *matched;
GList *remaining;
+ gint32 tree_offset;
GtkCssSelectorTree *tree;
GtkCssSelectorRuleSetInfo *info;
- GtkCssSelector *max_selector;
+ GtkCssSelector max_selector;
GHashTableIter iter;
guint max_count;
gpointer key, value;
GPtrArray *exact_matches;
+ gint32 res;
if (infos == NULL)
- return NULL;
+ return GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
+
+ ht = gtk_css_selectors_count_initial_init ();
for (l = infos; l != NULL; l = l->next)
{
as that makes it possible to skip the largest amount of checks later */
max_count = 0;
- max_selector = NULL;
g_hash_table_iter_init (&iter, ht);
while (g_hash_table_iter_next (&iter, &key, &value))
{
- if (GPOINTER_TO_UINT (value) > max_count)
+ GtkCssSelector *selector = key;
+ if (GPOINTER_TO_UINT (value) > max_count ||
+ (GPOINTER_TO_UINT (value) == max_count &&
+ gtk_css_selector_compare_one (selector, &max_selector) < 0))
{
max_count = GPOINTER_TO_UINT (value);
- max_selector = key;
+ max_selector = *selector;
}
}
matched = NULL;
remaining = NULL;
- tree = g_new0 (GtkCssSelectorTree, 1);
- tree->parent = parent;
- tree->selector = *max_selector;
+ tree = alloc_tree (array, &tree_offset);
+ tree->parent_offset = parent_offset;
+ tree->selector = max_selector;
exact_matches = NULL;
for (l = infos; l != NULL; l = l->next)
{
info = l->data;
- if (gtk_css_selectors_has_initial_selector (info->current_selector, &tree->selector))
+ if (gtk_css_selectors_has_initial_selector (info->current_selector, &max_selector))
{
- info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &tree->selector);
+ info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &max_selector);
if (info->current_selector == NULL)
{
/* Matches current node */
if (exact_matches == NULL)
exact_matches = g_ptr_array_new ();
g_ptr_array_add (exact_matches, info->match);
+ if (info->selector_match != NULL)
+ *info->selector_match = GUINT_TO_POINTER (tree_offset);
}
else
matched = g_list_prepend (matched, info);
if (exact_matches)
{
g_ptr_array_add (exact_matches, NULL); /* Null terminate */
- tree->matches = g_ptr_array_free (exact_matches, FALSE);
+ res = array->len;
+ g_byte_array_append (array, (guint8 *)exact_matches->pdata,
+ exact_matches->len * sizeof (gpointer));
+ g_ptr_array_free (exact_matches, TRUE);
}
+ else
+ res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
+ get_tree (array, tree_offset)->matches_offset = res;
- if (matched)
- tree->previous = subdivide_infos (matched, tree);
+ res = subdivide_infos (array, matched, tree_offset);
+ get_tree (array, tree_offset)->previous_offset = res;
- if (remaining)
- tree->siblings = subdivide_infos (remaining, parent);
+ res = subdivide_infos (array, remaining, parent_offset);
+ get_tree (array, tree_offset)->sibling_offset = res;
- return tree;
+ g_list_free (matched);
+ g_list_free (remaining);
+ g_hash_table_unref (ht);
+
+ return tree_offset;
}
struct _GtkCssSelectorTreeBuilder {
void
_gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
GtkCssSelector *selectors,
+ GtkCssSelectorTree **selector_match,
gpointer match)
{
GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
info->match = match;
info->current_selector = selectors;
+ info->selector_match = selector_match;
builder->infos = g_list_prepend (builder->infos, info);
}
+/* Convert all offsets to node-relative */
+static void
+fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
+{
+ while (tree != NULL)
+ {
+ if (tree->parent_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ tree->parent_offset -= ((guint8 *)tree - data);
+
+ if (tree->previous_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ tree->previous_offset -= ((guint8 *)tree - data);
+
+ if (tree->sibling_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ tree->sibling_offset -= ((guint8 *)tree - data);
+
+ if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
+ tree->matches_offset -= ((guint8 *)tree - data);
+
+ fixup_offsets ((GtkCssSelectorTree *)gtk_css_selector_tree_get_previous (tree), data);
+
+ tree = (GtkCssSelectorTree *)gtk_css_selector_tree_get_sibling (tree);
+ }
+}
+
GtkCssSelectorTree *
_gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
{
GtkCssSelectorTree *tree;
+ GByteArray *array;
+ guint8 *data;
+ guint len;
+ GList *l;
+ GtkCssSelectorRuleSetInfo *info;
+
+ array = g_byte_array_new ();
+ subdivide_infos (array, builder->infos, GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET);
- tree = subdivide_infos (builder->infos, NULL);
+ len = array->len;
+ data = g_byte_array_free (array, FALSE);
+
+ /* shrink to final size */
+ data = g_realloc (data, len);
+
+ tree = (GtkCssSelectorTree *)data;
+
+ fixup_offsets (tree, data);
+
+ /* Convert offsets to final pointers */
+ for (l = builder->infos; l != NULL; l = l->next)
+ {
+ info = l->data;
+ if (info->selector_match)
+ *info->selector_match = (GtkCssSelectorTree *)(data + GPOINTER_TO_UINT (*info->selector_match));
+ }
#ifdef PRINT_TREE
{