1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 #include "gtkcssselectorprivate.h"
25 #include "gtkcssprovider.h"
26 #include "gtkstylecontextprivate.h"
28 /* When checking for changes via the tree we need to know if a rule further
29 down the tree matched, because if so we need to add "our bit" to the
30 Change. For instance in a a match like *.class:active we'll
31 get a tree that first checks :active, if that matches we continue down
32 to the tree, and if we get a match we add CHANGE_CLASS. However, the
33 end of the tree where we have a match is an ANY which doesn't actually
34 modify the change, so we don't know if we have a match or not. We fix
35 this by setting GTK_CSS_CHANGE_GOT_MATCH which lets us guarantee
36 that change != 0 on any match. */
37 #define GTK_CSS_CHANGE_GOT_MATCH GTK_CSS_CHANGE_RESERVED_BIT
39 typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
41 struct _GtkCssSelectorClass {
44 void (* print) (const GtkCssSelector *selector,
46 gboolean (* match) (const GtkCssSelector *selector,
47 const GtkCssMatcher *matcher);
48 void (* tree_match) (const GtkCssSelectorTree *tree,
49 const GtkCssMatcher *matcher,
51 GtkCssChange (* get_change) (const GtkCssSelector *selector,
52 GtkCssChange previous_change);
53 GtkCssChange (* tree_get_change) (const GtkCssSelectorTree *tree,
54 const GtkCssMatcher *matcher);
55 int (* compare_one) (const GtkCssSelector *a,
56 const GtkCssSelector *b);
58 guint increase_id_specificity :1;
59 guint increase_class_specificity :1;
60 guint increase_element_specificity :1;
62 guint must_keep_order :1; /* Due to region weirdness these must be kept before a DESCENDANT, so don't reorder */
65 struct _GtkCssSelector
67 const GtkCssSelectorClass *class; /* type of check this selector does */
68 gconstpointer data; /* data for matching:
69 - interned string for CLASS, NAME and ID
70 - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
73 #define GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET G_MAXINT32
74 struct _GtkCssSelectorTree
76 GtkCssSelector selector;
78 gint32 previous_offset;
79 gint32 sibling_offset;
80 gint32 matches_offset; /* pointers that we return as matches if selector matches */
84 gtk_css_selector_equal (const GtkCssSelector *a,
85 const GtkCssSelector *b)
88 a->class == b->class &&
93 gtk_css_selector_hash (const GtkCssSelector *selector)
95 return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
99 gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
101 if (tree->matches_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
104 return (gpointer *) ((guint8 *)tree + tree->matches_offset);
108 gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
114 matches = gtk_css_selector_tree_get_matches (tree);
117 for (i = 0; matches[i] != NULL; i++)
118 g_hash_table_insert (res, matches[i], matches[i]);
123 gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
124 const GtkCssMatcher *matcher,
130 tree->selector.class->tree_match (tree, matcher, res);
134 gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
135 const GtkCssMatcher *matcher)
140 return tree->selector.class->tree_get_change (tree, matcher);
144 gtk_css_selector_match (const GtkCssSelector *selector,
145 const GtkCssMatcher *matcher)
147 if (selector == NULL)
150 return selector->class->match (selector, matcher);
154 gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
156 if (a->class != b->class)
157 return strcmp (a->class->name, b->class->name);
159 return a->class->compare_one (a, b);
162 static const GtkCssSelector *
163 gtk_css_selector_previous (const GtkCssSelector *selector)
165 selector = selector + 1;
167 return selector->class ? selector : NULL;
170 static const GtkCssSelectorTree *
171 gtk_css_selector_tree_at_offset (const GtkCssSelectorTree *tree,
174 if (offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
177 return (GtkCssSelectorTree *) ((guint8 *)tree + offset);
180 static const GtkCssSelectorTree *
181 gtk_css_selector_tree_get_parent (const GtkCssSelectorTree *tree)
183 return gtk_css_selector_tree_at_offset (tree, tree->parent_offset);
186 static const GtkCssSelectorTree *
187 gtk_css_selector_tree_get_previous (const GtkCssSelectorTree *tree)
189 return gtk_css_selector_tree_at_offset (tree, tree->previous_offset);
192 static const GtkCssSelectorTree *
193 gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
195 return gtk_css_selector_tree_at_offset (tree, tree->sibling_offset);
199 gtk_css_selector_tree_match_previous (const GtkCssSelectorTree *tree,
200 const GtkCssMatcher *matcher,
203 const GtkCssSelectorTree *prev;
205 for (prev = gtk_css_selector_tree_get_previous (tree);
207 prev = gtk_css_selector_tree_get_sibling (prev))
208 gtk_css_selector_tree_match (prev, matcher, res);
212 gtk_css_selector_tree_get_previous_change (const GtkCssSelectorTree *tree,
213 const GtkCssMatcher *matcher)
215 GtkCssChange previous_change = 0;
216 const GtkCssSelectorTree *prev;
218 for (prev = gtk_css_selector_tree_get_previous (tree);
220 prev = gtk_css_selector_tree_get_sibling (prev))
221 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
223 return previous_change;
229 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
232 g_string_append_c (string, ' ');
236 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
237 const GtkCssMatcher *matcher)
239 GtkCssMatcher ancestor;
241 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
245 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
253 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
254 const GtkCssMatcher *matcher,
257 GtkCssMatcher ancestor;
259 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
263 gtk_css_selector_tree_match_previous (tree, matcher, res);
265 /* any matchers are dangerous here, as we may loop forever, but
266 we can terminate now as all possible matches have already been added */
267 if (_gtk_css_matcher_matches_any (matcher))
273 gtk_css_selector_descendant_tree_get_change (const GtkCssSelectorTree *tree,
274 const GtkCssMatcher *matcher)
276 GtkCssMatcher ancestor;
277 GtkCssChange change, previous_change;
281 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
285 previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
287 /* any matchers are dangerous here, as we may loop forever, but
288 we can terminate now as all possible matches have already been added */
289 if (_gtk_css_matcher_matches_any (matcher))
293 if (previous_change != 0)
294 change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
300 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
301 const GtkCssSelector *b)
307 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
309 return _gtk_css_change_for_child (previous_change);
312 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
314 gtk_css_selector_descendant_print,
315 gtk_css_selector_descendant_match,
316 gtk_css_selector_descendant_tree_match,
317 gtk_css_selector_descendant_get_change,
318 gtk_css_selector_descendant_tree_get_change,
319 gtk_css_selector_descendant_compare_one,
320 FALSE, FALSE, FALSE, FALSE, FALSE
326 gtk_css_selector_child_print (const GtkCssSelector *selector,
329 g_string_append (string, " > ");
333 gtk_css_selector_child_match (const GtkCssSelector *selector,
334 const GtkCssMatcher *matcher)
336 GtkCssMatcher parent;
338 if (!_gtk_css_matcher_get_parent (&parent, matcher))
341 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
345 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
346 const GtkCssMatcher *matcher,
349 GtkCssMatcher parent;
351 if (!_gtk_css_matcher_get_parent (&parent, matcher))
354 gtk_css_selector_tree_match_previous (tree, &parent, res);
359 gtk_css_selector_child_tree_get_change (const GtkCssSelectorTree *tree,
360 const GtkCssMatcher *matcher)
362 GtkCssMatcher parent;
363 GtkCssChange change, previous_change;
365 if (!_gtk_css_matcher_get_parent (&parent, matcher))
370 previous_change = gtk_css_selector_tree_get_previous_change (tree, &parent);
372 if (previous_change != 0)
373 change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
379 gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
381 return _gtk_css_change_for_child (previous_change);
385 gtk_css_selector_child_compare_one (const GtkCssSelector *a,
386 const GtkCssSelector *b)
391 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
393 gtk_css_selector_child_print,
394 gtk_css_selector_child_match,
395 gtk_css_selector_child_tree_match,
396 gtk_css_selector_child_get_change,
397 gtk_css_selector_child_tree_get_change,
398 gtk_css_selector_child_compare_one,
399 FALSE, FALSE, FALSE, FALSE, FALSE
405 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
408 g_string_append (string, " ~ ");
412 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
413 const GtkCssMatcher *matcher)
415 GtkCssMatcher previous;
417 while (_gtk_css_matcher_get_previous (&previous, matcher))
421 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
429 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
430 const GtkCssMatcher *matcher,
433 GtkCssMatcher previous;
435 while (_gtk_css_matcher_get_previous (&previous, matcher))
439 gtk_css_selector_tree_match_previous (tree, matcher, res);
441 /* any matchers are dangerous here, as we may loop forever, but
442 we can terminate now as all possible matches have already been added */
443 if (_gtk_css_matcher_matches_any (matcher))
449 gtk_css_selector_sibling_tree_get_change (const GtkCssSelectorTree *tree,
450 const GtkCssMatcher *matcher)
452 GtkCssMatcher previous;
453 GtkCssChange change, previous_change;
458 while (_gtk_css_matcher_get_previous (&previous, matcher))
462 previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
464 /* any matchers are dangerous here, as we may loop forever, but
465 we can terminate now as all possible matches have already been added */
466 if (_gtk_css_matcher_matches_any (matcher))
470 if (previous_change != 0)
471 change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
477 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
479 return _gtk_css_change_for_sibling (previous_change);
483 gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
484 const GtkCssSelector *b)
490 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
492 gtk_css_selector_sibling_print,
493 gtk_css_selector_sibling_match,
494 gtk_css_selector_sibling_tree_match,
495 gtk_css_selector_sibling_get_change,
496 gtk_css_selector_sibling_tree_get_change,
497 gtk_css_selector_sibling_compare_one,
498 FALSE, FALSE, FALSE, FALSE, FALSE
504 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
507 g_string_append (string, " + ");
511 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
512 const GtkCssMatcher *matcher)
514 GtkCssMatcher previous;
516 if (!_gtk_css_matcher_get_previous (&previous, matcher))
519 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
523 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
524 const GtkCssMatcher *matcher,
527 GtkCssMatcher previous;
529 if (!_gtk_css_matcher_get_previous (&previous, matcher))
534 gtk_css_selector_tree_match_previous (tree, matcher, res);
538 gtk_css_selector_adjacent_tree_get_change (const GtkCssSelectorTree *tree,
539 const GtkCssMatcher *matcher)
541 GtkCssMatcher previous;
542 GtkCssChange change, previous_change;
544 if (!_gtk_css_matcher_get_previous (&previous, matcher))
549 previous_change = gtk_css_selector_tree_get_previous_change (tree, &previous);
551 if (previous_change != 0)
552 change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
558 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
560 return _gtk_css_change_for_sibling (previous_change);
564 gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
565 const GtkCssSelector *b)
570 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
572 gtk_css_selector_adjacent_print,
573 gtk_css_selector_adjacent_match,
574 gtk_css_selector_adjacent_tree_match,
575 gtk_css_selector_adjacent_get_change,
576 gtk_css_selector_adjacent_tree_get_change,
577 gtk_css_selector_adjacent_compare_one,
578 FALSE, FALSE, FALSE, FALSE, FALSE
584 gtk_css_selector_any_print (const GtkCssSelector *selector,
587 g_string_append_c (string, '*');
591 gtk_css_selector_any_match (const GtkCssSelector *selector,
592 const GtkCssMatcher *matcher)
594 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
597 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
598 _gtk_css_matcher_has_regions (matcher))
600 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
604 return gtk_css_selector_match (previous, matcher);
608 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
609 const GtkCssMatcher *matcher,
612 const GtkCssSelectorTree *prev;
614 gtk_css_selector_tree_found_match (tree, res);
616 for (prev = gtk_css_selector_tree_get_previous (tree);
618 prev = gtk_css_selector_tree_get_sibling (prev))
620 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
621 _gtk_css_matcher_has_regions (matcher))
622 gtk_css_selector_tree_match_previous (prev, matcher, res);
624 gtk_css_selector_tree_match (prev, matcher, res);
629 gtk_css_selector_any_tree_get_change (const GtkCssSelectorTree *tree,
630 const GtkCssMatcher *matcher)
632 const GtkCssSelectorTree *prev;
633 GtkCssChange change, previous_change;
637 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
638 change |= GTK_CSS_CHANGE_GOT_MATCH;
641 for (prev = gtk_css_selector_tree_get_previous (tree);
643 prev = gtk_css_selector_tree_get_sibling (prev))
645 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
646 _gtk_css_matcher_has_regions (matcher))
647 previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
649 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
652 if (previous_change != 0)
653 change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
660 gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
662 return previous_change;
666 gtk_css_selector_any_compare_one (const GtkCssSelector *a,
667 const GtkCssSelector *b)
672 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
674 gtk_css_selector_any_print,
675 gtk_css_selector_any_match,
676 gtk_css_selector_any_tree_match,
677 gtk_css_selector_any_get_change,
678 gtk_css_selector_any_tree_get_change,
679 gtk_css_selector_any_compare_one,
680 FALSE, FALSE, FALSE, TRUE, TRUE
690 static GHashTable *type_refs_ht = NULL;
691 static guint type_refs_last_serial = 0;
693 static TypeReference *
694 get_type_reference (const char *name)
699 if (type_refs_ht == NULL)
700 type_refs_ht = g_hash_table_new (g_str_hash, g_str_equal);
702 ref = g_hash_table_lookup (type_refs_ht, name);
707 ref = g_slice_new (TypeReference);
708 ref->name = g_intern_string (name);
709 ref->type = g_type_from_name (ref->name);
711 g_hash_table_insert (type_refs_ht,
712 (gpointer)ref->name, ref);
718 update_type_references (void)
724 serial = g_type_get_type_registration_serial ();
726 if (serial == type_refs_last_serial)
729 type_refs_last_serial = serial;
731 if (type_refs_ht == NULL)
734 g_hash_table_iter_init (&iter, type_refs_ht);
735 while (g_hash_table_iter_next (&iter,
738 TypeReference *ref = value;
739 if (ref->type == G_TYPE_INVALID)
740 ref->type = g_type_from_name (ref->name);
745 gtk_css_selector_name_print (const GtkCssSelector *selector,
748 g_string_append (string, ((TypeReference *)selector->data)->name);
752 gtk_css_selector_name_match (const GtkCssSelector *selector,
753 const GtkCssMatcher *matcher)
755 if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)selector->data)->type))
758 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
762 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
763 const GtkCssMatcher *matcher,
766 if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)tree->selector.data)->type))
769 gtk_css_selector_tree_found_match (tree, res);
771 gtk_css_selector_tree_match_previous (tree, matcher, res);
775 gtk_css_selector_name_tree_get_change (const GtkCssSelectorTree *tree,
776 const GtkCssMatcher *matcher)
778 GtkCssChange change, previous_change;
780 if (!_gtk_css_matcher_has_type (matcher, ((TypeReference *)tree->selector.data)->type))
785 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
786 change |= GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
788 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
791 change |= previous_change | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
798 gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
800 return previous_change | GTK_CSS_CHANGE_NAME;
804 gtk_css_selector_name_compare_one (const GtkCssSelector *a,
805 const GtkCssSelector *b)
807 return strcmp (((TypeReference *)a->data)->name,
808 ((TypeReference *)b->data)->name);
811 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
813 gtk_css_selector_name_print,
814 gtk_css_selector_name_match,
815 gtk_css_selector_name_tree_match,
816 gtk_css_selector_name_get_change,
817 gtk_css_selector_name_tree_get_change,
818 gtk_css_selector_name_compare_one,
819 FALSE, FALSE, TRUE, TRUE, FALSE
825 gtk_css_selector_region_print (const GtkCssSelector *selector,
828 g_string_append (string, selector->data);
832 gtk_css_selector_region_match (const GtkCssSelector *selector,
833 const GtkCssMatcher *matcher)
835 const GtkCssSelector *previous;
837 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
840 previous = gtk_css_selector_previous (selector);
841 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
842 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
845 return gtk_css_selector_match (previous, matcher);
849 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
850 const GtkCssMatcher *matcher,
853 const GtkCssSelectorTree *prev;
855 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
858 gtk_css_selector_tree_found_match (tree, res);
860 for (prev = gtk_css_selector_tree_get_previous (tree);
862 prev = gtk_css_selector_tree_get_sibling (prev))
864 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
865 gtk_css_selector_tree_match_previous (prev, matcher, res);
867 gtk_css_selector_tree_match (prev, matcher, res);
872 gtk_css_selector_region_tree_get_change (const GtkCssSelectorTree *tree,
873 const GtkCssMatcher *matcher)
875 const GtkCssSelectorTree *prev;
876 GtkCssChange change, previous_change;
878 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
883 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
884 change |= GTK_CSS_CHANGE_REGION | GTK_CSS_CHANGE_GOT_MATCH;
887 for (prev = gtk_css_selector_tree_get_previous (tree);
889 prev = gtk_css_selector_tree_get_sibling (prev))
891 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
892 previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
894 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
897 if (previous_change != 0)
899 previous_change |= GTK_CSS_CHANGE_REGION;
900 previous_change |= _gtk_css_change_for_child (previous_change);
901 change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
908 gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
912 change = previous_change;
913 change |= GTK_CSS_CHANGE_REGION;
914 change |= _gtk_css_change_for_child (change);
920 gtk_css_selector_region_compare_one (const GtkCssSelector *a,
921 const GtkCssSelector *b)
923 return strcmp (a->data, b->data);
926 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
928 gtk_css_selector_region_print,
929 gtk_css_selector_region_match,
930 gtk_css_selector_region_tree_match,
931 gtk_css_selector_region_get_change,
932 gtk_css_selector_region_tree_get_change,
933 gtk_css_selector_region_compare_one,
934 FALSE, FALSE, TRUE, TRUE, TRUE
940 gtk_css_selector_class_print (const GtkCssSelector *selector,
943 g_string_append_c (string, '.');
944 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
948 gtk_css_selector_class_match (const GtkCssSelector *selector,
949 const GtkCssMatcher *matcher)
951 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
954 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
958 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
959 const GtkCssMatcher *matcher,
962 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
965 gtk_css_selector_tree_found_match (tree, res);
967 gtk_css_selector_tree_match_previous (tree, matcher, res);
971 gtk_css_selector_class_tree_get_change (const GtkCssSelectorTree *tree,
972 const GtkCssMatcher *matcher)
974 GtkCssChange change, previous_change;
976 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
981 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
982 change |= GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
984 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
986 if (previous_change != 0)
987 change |= previous_change | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
993 gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
995 return previous_change | GTK_CSS_CHANGE_CLASS;
999 gtk_css_selector_class_compare_one (const GtkCssSelector *a,
1000 const GtkCssSelector *b)
1002 return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
1003 g_quark_to_string (GPOINTER_TO_UINT (b->data)));
1006 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
1008 gtk_css_selector_class_print,
1009 gtk_css_selector_class_match,
1010 gtk_css_selector_class_tree_match,
1011 gtk_css_selector_class_get_change,
1012 gtk_css_selector_class_tree_get_change,
1013 gtk_css_selector_class_compare_one,
1014 FALSE, TRUE, FALSE, TRUE, FALSE
1020 gtk_css_selector_id_print (const GtkCssSelector *selector,
1023 g_string_append_c (string, '#');
1024 g_string_append (string, selector->data);
1028 gtk_css_selector_id_match (const GtkCssSelector *selector,
1029 const GtkCssMatcher *matcher)
1031 if (!_gtk_css_matcher_has_id (matcher, selector->data))
1034 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
1038 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
1039 const GtkCssMatcher *matcher,
1042 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
1045 gtk_css_selector_tree_found_match (tree, res);
1047 gtk_css_selector_tree_match_previous (tree, matcher, res);
1051 gtk_css_selector_id_tree_get_change (const GtkCssSelectorTree *tree,
1052 const GtkCssMatcher *matcher)
1054 GtkCssChange change, previous_change;
1056 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
1061 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1062 change |= GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
1064 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
1066 if (previous_change != 0)
1067 change |= previous_change | GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
1073 gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1075 return previous_change | GTK_CSS_CHANGE_ID;
1080 gtk_css_selector_id_compare_one (const GtkCssSelector *a,
1081 const GtkCssSelector *b)
1083 return strcmp (a->data, b->data);
1086 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
1088 gtk_css_selector_id_print,
1089 gtk_css_selector_id_match,
1090 gtk_css_selector_id_tree_match,
1091 gtk_css_selector_id_get_change,
1092 gtk_css_selector_id_tree_get_change,
1093 gtk_css_selector_id_compare_one,
1094 TRUE, FALSE, FALSE, TRUE, FALSE
1097 /* PSEUDOCLASS FOR STATE */
1100 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
1103 static const char * state_names[] = {
1116 state = GPOINTER_TO_UINT (selector->data);
1117 g_string_append_c (string, ':');
1119 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
1121 if (state == (1 << i))
1123 g_string_append (string, state_names[i]);
1128 g_assert_not_reached ();
1132 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
1133 const GtkCssMatcher *matcher)
1135 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
1137 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
1140 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
1144 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
1145 const GtkCssMatcher *matcher,
1148 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
1150 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
1153 gtk_css_selector_tree_found_match (tree, res);
1155 gtk_css_selector_tree_match_previous (tree, matcher, res);
1159 gtk_css_selector_pseudoclass_state_tree_get_change (const GtkCssSelectorTree *tree,
1160 const GtkCssMatcher *matcher)
1162 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
1163 GtkCssChange change, previous_change;
1165 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
1170 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1171 change |= GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
1173 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
1175 if (previous_change != 0)
1176 change |= previous_change | GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
1182 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1184 return previous_change | GTK_CSS_CHANGE_STATE;
1188 gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
1189 const GtkCssSelector *b)
1191 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1194 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
1195 "pseudoclass-state",
1196 gtk_css_selector_pseudoclass_state_print,
1197 gtk_css_selector_pseudoclass_state_match,
1198 gtk_css_selector_pseudoclass_state_tree_match,
1199 gtk_css_selector_pseudoclass_state_get_change,
1200 gtk_css_selector_pseudoclass_state_tree_get_change,
1201 gtk_css_selector_pseudoclass_state_compare_one,
1202 FALSE, TRUE, FALSE, TRUE, FALSE
1205 /* PSEUDOCLASS FOR POSITION */
1213 #define POSITION_TYPE_BITS 2
1214 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
1216 static gconstpointer
1217 encode_position (PositionType type,
1224 gssize type :POSITION_TYPE_BITS;
1225 gssize a :POSITION_NUMBER_BITS;
1226 gssize b :POSITION_NUMBER_BITS;
1229 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
1231 g_assert (type < (1 << POSITION_TYPE_BITS));
1233 result.data.type = type;
1241 decode_position (const GtkCssSelector *selector,
1249 gssize type :POSITION_TYPE_BITS;
1250 gssize a :POSITION_NUMBER_BITS;
1251 gssize b :POSITION_NUMBER_BITS;
1254 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
1256 result.p = selector->data;
1258 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
1264 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
1270 decode_position (selector, &type, &a, &b);
1273 case POSITION_FORWARD:
1277 g_string_append (string, ":first-child");
1279 g_string_append_printf (string, ":nth-child(%d)", b);
1281 else if (a == 2 && b == 0)
1282 g_string_append (string, ":nth-child(even)");
1283 else if (a == 2 && b == 1)
1284 g_string_append (string, ":nth-child(odd)");
1287 g_string_append (string, ":nth-child(");
1289 g_string_append (string, "n");
1291 g_string_append (string, "-n");
1293 g_string_append_printf (string, "%dn", a);
1295 g_string_append_printf (string, "+%d)", b);
1297 g_string_append_printf (string, "%d)", b);
1299 g_string_append (string, ")");
1302 case POSITION_BACKWARD:
1306 g_string_append (string, ":last-child");
1308 g_string_append_printf (string, ":nth-last-child(%d)", b);
1310 else if (a == 2 && b == 0)
1311 g_string_append (string, ":nth-last-child(even)");
1312 else if (a == 2 && b == 1)
1313 g_string_append (string, ":nth-last-child(odd)");
1316 g_string_append (string, ":nth-last-child(");
1318 g_string_append (string, "n");
1320 g_string_append (string, "-n");
1322 g_string_append_printf (string, "%dn", a);
1324 g_string_append_printf (string, "+%d)", b);
1326 g_string_append_printf (string, "%d)", b);
1328 g_string_append (string, ")");
1332 g_string_append (string, ":only-child");
1334 case POSITION_SORTED:
1335 g_string_append (string, ":sorted");
1338 g_assert_not_reached ();
1344 get_selector_flags_for_position_region_match (const GtkCssSelector *selector, GtkRegionFlags *selector_flags)
1349 decode_position (selector, &type, &a, &b);
1352 case POSITION_FORWARD:
1353 if (a == 0 && b == 1)
1354 *selector_flags = GTK_REGION_FIRST;
1355 else if (a == 2 && b == 0)
1356 *selector_flags = GTK_REGION_EVEN;
1357 else if (a == 2 && b == 1)
1358 *selector_flags = GTK_REGION_ODD;
1362 case POSITION_BACKWARD:
1363 if (a == 0 && b == 1)
1364 *selector_flags = GTK_REGION_LAST;
1369 *selector_flags = GTK_REGION_ONLY;
1371 case POSITION_SORTED:
1372 *selector_flags = GTK_REGION_SORTED;
1375 g_assert_not_reached ();
1383 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
1384 const GtkCssMatcher *matcher)
1386 GtkRegionFlags selector_flags;
1387 const GtkCssSelector *previous;
1389 if (!get_selector_flags_for_position_region_match (selector, &selector_flags))
1392 selector = gtk_css_selector_previous (selector);
1394 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
1397 previous = gtk_css_selector_previous (selector);
1398 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
1399 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
1402 return gtk_css_selector_match (previous, matcher);
1406 get_position_match (const GtkCssSelector *selector,
1407 const GtkCssMatcher *matcher)
1412 decode_position (selector, &type, &a, &b);
1415 case POSITION_FORWARD:
1416 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1419 case POSITION_BACKWARD:
1420 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1424 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1425 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1428 case POSITION_SORTED:
1431 g_assert_not_reached ();
1439 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
1440 const GtkCssMatcher *matcher)
1442 const GtkCssSelector *previous;
1444 previous = gtk_css_selector_previous (selector);
1445 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
1446 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
1448 if (!get_position_match (selector, matcher))
1451 return gtk_css_selector_match (previous, matcher);
1455 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
1456 const GtkCssSelectorTree *prev,
1457 const GtkCssMatcher *matcher,
1460 const GtkCssSelectorTree *prev2;
1461 GtkRegionFlags selector_flags;
1463 if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
1466 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1469 gtk_css_selector_tree_found_match (prev, res);
1471 for (prev2 = gtk_css_selector_tree_get_previous (prev);
1473 prev2 = gtk_css_selector_tree_get_sibling (prev2))
1475 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1476 gtk_css_selector_tree_match (gtk_css_selector_tree_get_previous (prev2), matcher, res);
1477 gtk_css_selector_tree_match (prev2, matcher, res);
1482 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1483 const GtkCssMatcher *matcher,
1486 const GtkCssSelectorTree *prev;
1488 for (prev = gtk_css_selector_tree_get_previous (tree);
1490 prev = gtk_css_selector_tree_get_sibling (prev))
1492 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1493 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1496 if (!get_position_match (&tree->selector, matcher))
1499 gtk_css_selector_tree_found_match (tree, res);
1501 for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
1503 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1504 gtk_css_selector_tree_match (prev, matcher, res);
1509 gtk_css_selector_pseudoclass_position_tree_get_change_for_region (const GtkCssSelectorTree *tree,
1510 const GtkCssSelectorTree *prev,
1511 const GtkCssMatcher *matcher)
1513 const GtkCssSelectorTree *prev2;
1514 GtkRegionFlags selector_flags;
1515 GtkCssChange change, previous_change;
1517 if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
1520 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1524 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1525 change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1527 previous_change = 0;
1528 for (prev2 = gtk_css_selector_tree_get_previous (prev);
1530 prev2 = gtk_css_selector_tree_get_sibling (prev2))
1532 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1533 previous_change |= gtk_css_selector_tree_get_change (gtk_css_selector_tree_get_previous (prev2), matcher);
1534 previous_change |= gtk_css_selector_tree_get_change (prev2, matcher);
1537 if (previous_change != 0)
1538 change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1544 gtk_css_selector_pseudoclass_position_tree_get_change (const GtkCssSelectorTree *tree,
1545 const GtkCssMatcher *matcher)
1547 const GtkCssSelectorTree *prev;
1548 GtkCssChange change, previous_change;
1552 for (prev = gtk_css_selector_tree_get_previous (tree);
1554 prev = gtk_css_selector_tree_get_sibling (prev))
1556 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1557 change |= gtk_css_selector_pseudoclass_position_tree_get_change_for_region (tree, prev, matcher);
1560 if (!get_position_match (&tree->selector, matcher))
1563 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1564 change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1566 previous_change = 0;
1567 for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
1569 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1570 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
1573 if (previous_change != 0)
1574 change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1580 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1582 return previous_change | GTK_CSS_CHANGE_POSITION;
1586 gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
1587 const GtkCssSelector *b)
1589 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1592 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1593 "pseudoclass-position",
1594 gtk_css_selector_pseudoclass_position_print,
1595 gtk_css_selector_pseudoclass_position_match,
1596 gtk_css_selector_pseudoclass_position_tree_match,
1597 gtk_css_selector_pseudoclass_position_get_change,
1598 gtk_css_selector_pseudoclass_position_tree_get_change,
1599 gtk_css_selector_pseudoclass_position_compare_one,
1600 FALSE, TRUE, FALSE, TRUE, TRUE
1606 gtk_css_selector_size (const GtkCssSelector *selector)
1612 selector = gtk_css_selector_previous (selector);
1619 static GtkCssSelector *
1620 gtk_css_selector_new (const GtkCssSelectorClass *class,
1621 GtkCssSelector *selector,
1626 size = gtk_css_selector_size (selector);
1627 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1629 selector[1].class = NULL;
1631 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1633 selector->class = class;
1634 selector->data = data;
1639 static GtkCssSelector *
1640 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1644 name = _gtk_css_parser_try_name (parser, FALSE);
1648 _gtk_css_parser_error (parser, "Expected a valid name for class");
1650 _gtk_css_selector_free (selector);
1654 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1656 GUINT_TO_POINTER (g_quark_from_string (name)));
1663 static GtkCssSelector *
1664 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1668 name = _gtk_css_parser_try_name (parser, FALSE);
1672 _gtk_css_parser_error (parser, "Expected a valid name for id");
1674 _gtk_css_selector_free (selector);
1678 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1680 g_intern_string (name));
1687 static GtkCssSelector *
1688 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1689 GtkCssSelector *selector,
1694 if (!_gtk_css_parser_try (parser, "(", TRUE))
1696 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1698 _gtk_css_selector_free (selector);
1702 if (_gtk_css_parser_try (parser, "even", TRUE))
1707 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1712 else if (type == POSITION_FORWARD &&
1713 _gtk_css_parser_try (parser, "first", TRUE))
1718 else if (type == POSITION_FORWARD &&
1719 _gtk_css_parser_try (parser, "last", TRUE))
1723 type = POSITION_BACKWARD;
1729 if (_gtk_css_parser_try (parser, "+", TRUE))
1731 else if (_gtk_css_parser_try (parser, "-", TRUE))
1736 if (_gtk_css_parser_try_int (parser, &a))
1740 _gtk_css_parser_error (parser, "Expected an integer");
1742 _gtk_css_selector_free (selector);
1747 else if (_gtk_css_parser_has_prefix (parser, "n"))
1753 _gtk_css_parser_error (parser, "Expected an integer");
1755 _gtk_css_selector_free (selector);
1759 if (_gtk_css_parser_try (parser, "n", TRUE))
1761 if (_gtk_css_parser_try (parser, "+", TRUE))
1763 else if (_gtk_css_parser_try (parser, "-", TRUE))
1768 if (_gtk_css_parser_try_int (parser, &b))
1772 _gtk_css_parser_error (parser, "Expected an integer");
1774 _gtk_css_selector_free (selector);
1790 if (!_gtk_css_parser_try (parser, ")", FALSE))
1792 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1794 _gtk_css_selector_free (selector);
1798 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1800 encode_position (type, a, b));
1805 static GtkCssSelector *
1806 parse_selector_pseudo_class (GtkCssParser *parser,
1807 GtkCssSelector *selector)
1809 static const struct {
1811 GtkStateFlags state_flag;
1812 PositionType position_type;
1815 } pseudo_classes[] = {
1816 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1817 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1818 { "only-child", 0, POSITION_ONLY, 0, 0 },
1819 { "sorted", 0, POSITION_SORTED, 0, 0 },
1820 { "active", GTK_STATE_FLAG_ACTIVE, },
1821 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1822 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1823 { "selected", GTK_STATE_FLAG_SELECTED, },
1824 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1825 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1826 { "focused", GTK_STATE_FLAG_FOCUSED, },
1827 { "focus", GTK_STATE_FLAG_FOCUSED, },
1828 { "backdrop", GTK_STATE_FLAG_BACKDROP, },
1829 { "dir(ltr)", GTK_STATE_FLAG_DIR_LTR, },
1830 { "dir(rtl)", GTK_STATE_FLAG_DIR_RTL, }
1834 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1835 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1836 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1837 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1839 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1841 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1843 if (pseudo_classes[i].state_flag)
1844 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1846 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1848 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1850 encode_position (pseudo_classes[i].position_type,
1851 pseudo_classes[i].position_a,
1852 pseudo_classes[i].position_b));
1857 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1859 _gtk_css_selector_free (selector);
1863 static GtkCssSelector *
1864 try_parse_name (GtkCssParser *parser,
1865 GtkCssSelector *selector)
1869 name = _gtk_css_parser_try_ident (parser, FALSE);
1872 if (_gtk_style_context_check_region_name (name))
1873 selector = gtk_css_selector_new (>K_CSS_SELECTOR_REGION,
1875 g_intern_string (name));
1877 selector = gtk_css_selector_new (>K_CSS_SELECTOR_NAME,
1879 get_type_reference (name));
1882 else if (_gtk_css_parser_try (parser, "*", FALSE))
1883 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1888 static GtkCssSelector *
1889 parse_simple_selector (GtkCssParser *parser,
1890 GtkCssSelector *selector)
1892 guint size = gtk_css_selector_size (selector);
1894 selector = try_parse_name (parser, selector);
1897 if (_gtk_css_parser_try (parser, "#", FALSE))
1898 selector = parse_selector_id (parser, selector);
1899 else if (_gtk_css_parser_try (parser, ".", FALSE))
1900 selector = parse_selector_class (parser, selector);
1901 else if (_gtk_css_parser_try (parser, ":", FALSE))
1902 selector = parse_selector_pseudo_class (parser, selector);
1903 else if (gtk_css_selector_size (selector) == size)
1905 _gtk_css_parser_error (parser, "Expected a valid selector");
1907 _gtk_css_selector_free (selector);
1913 while (selector && !_gtk_css_parser_is_eof (parser));
1915 _gtk_css_parser_skip_whitespace (parser);
1921 _gtk_css_selector_parse (GtkCssParser *parser)
1923 GtkCssSelector *selector = NULL;
1925 while ((selector = parse_simple_selector (parser, selector)) &&
1926 !_gtk_css_parser_is_eof (parser) &&
1927 !_gtk_css_parser_begins_with (parser, ',') &&
1928 !_gtk_css_parser_begins_with (parser, '{'))
1930 if (_gtk_css_parser_try (parser, "+", TRUE))
1931 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1932 else if (_gtk_css_parser_try (parser, "~", TRUE))
1933 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1934 else if (_gtk_css_parser_try (parser, ">", TRUE))
1935 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1937 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1944 _gtk_css_selector_free (GtkCssSelector *selector)
1946 g_return_if_fail (selector != NULL);
1952 _gtk_css_selector_print (const GtkCssSelector *selector,
1955 const GtkCssSelector *previous;
1957 g_return_if_fail (selector != NULL);
1959 previous = gtk_css_selector_previous (selector);
1961 _gtk_css_selector_print (previous, str);
1963 selector->class->print (selector, str);
1967 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1971 g_return_val_if_fail (selector != NULL, NULL);
1973 string = g_string_new (NULL);
1975 _gtk_css_selector_print (selector, string);
1977 return g_string_free (string, FALSE);
1982 _gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree)
1984 GtkCssChange change = 0;
1986 update_type_references ();
1990 change = tree->selector.class->get_change (&tree->selector, change);
1991 tree = gtk_css_selector_tree_get_parent (tree);
1998 * _gtk_css_selector_matches:
1999 * @selector: the selector
2000 * @path: the path to check
2001 * @state: The state to match
2003 * Checks if the @selector matches the given @path. If @length is
2004 * smaller than the number of elements in @path, it is assumed that
2005 * only the first @length element of @path are valid and the rest
2006 * does not exist. This is useful for doing parent matches for the
2007 * 'inherit' keyword.
2009 * Returns: %TRUE if the selector matches @path
2012 _gtk_css_selector_matches (const GtkCssSelector *selector,
2013 const GtkCssMatcher *matcher)
2016 g_return_val_if_fail (selector != NULL, FALSE);
2017 g_return_val_if_fail (matcher != NULL, FALSE);
2019 update_type_references ();
2021 return gtk_css_selector_match (selector, matcher);
2024 /* Computes specificity according to CSS 2.1.
2025 * The arguments must be initialized to 0 */
2027 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
2032 for (; selector; selector = gtk_css_selector_previous (selector))
2034 const GtkCssSelectorClass *klass = selector->class;
2036 if (klass->increase_id_specificity)
2038 if (klass->increase_class_specificity)
2040 if (klass->increase_element_specificity)
2046 _gtk_css_selector_compare (const GtkCssSelector *a,
2047 const GtkCssSelector *b)
2049 guint a_ids = 0, a_classes = 0, a_elements = 0;
2050 guint b_ids = 0, b_classes = 0, b_elements = 0;
2053 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
2054 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
2056 compare = a_ids - b_ids;
2060 compare = a_classes - b_classes;
2064 return a_elements - b_elements;
2068 /******************** SelectorTree handling *****************/
2071 gtk_css_selectors_count_initial_init (void)
2073 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
2077 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
2079 if (!selector->class->is_simple || selector->class->must_keep_order)
2081 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
2082 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
2087 selector && selector->class->is_simple && !selector->class->must_keep_order;
2088 selector = gtk_css_selector_previous (selector))
2090 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
2091 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
2096 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
2098 if (!selector->class->is_simple || selector->class->must_keep_order)
2099 return gtk_css_selector_equal (selector, initial);
2102 selector && selector->class->is_simple && !selector->class->must_keep_order;
2103 selector = gtk_css_selector_previous (selector))
2105 if (gtk_css_selector_equal (selector, initial))
2112 static GtkCssSelector *
2113 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
2115 GtkCssSelector *found;
2118 /* If the initial simple selector is not first, move it there so we can skip it
2119 without losing any other selectors */
2120 if (!gtk_css_selector_equal (selector, initial))
2122 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
2124 if (gtk_css_selector_equal (found, initial))
2128 g_assert (found != NULL && found->class->is_simple);
2135 return (GtkCssSelector *)gtk_css_selector_previous (selector);
2139 direct_ptr_compare (const void *_a, const void *_b)
2141 gpointer *a = (gpointer *)_a;
2142 gpointer *b = (gpointer *)_b;
2151 _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
2152 const GtkCssMatcher *matcher)
2156 GHashTableIter iter;
2159 update_type_references ();
2161 res = g_hash_table_new (g_direct_hash, g_direct_equal);
2163 for (; tree != NULL;
2164 tree = gtk_css_selector_tree_get_sibling (tree))
2165 gtk_css_selector_tree_match (tree, matcher, res);
2167 array = g_ptr_array_sized_new (g_hash_table_size (res));
2169 g_hash_table_iter_init (&iter, res);
2170 while (g_hash_table_iter_next (&iter, &key, NULL))
2171 g_ptr_array_add (array, key);
2173 g_hash_table_destroy (res);
2175 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
2181 _gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
2182 const GtkCssMatcher *matcher)
2184 GtkCssChange change;
2188 for (; tree != NULL;
2189 tree = gtk_css_selector_tree_get_sibling (tree))
2190 change |= gtk_css_selector_tree_get_change (tree, matcher);
2192 /* Never return reserved bit set */
2193 return change & ~GTK_CSS_CHANGE_RESERVED_BIT;
2198 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
2200 gboolean first = TRUE;
2203 for (; tree != NULL; tree = tree->siblings, first = FALSE)
2206 g_string_append (str, prefix);
2211 g_string_append (str, "─┬─");
2213 g_string_append (str, "───");
2218 g_string_append (str, " ├─");
2220 g_string_append (str, " └─");
2224 tree->selector.class->print (&tree->selector, str);
2225 len = str->len - len;
2227 if (gtk_css_selector_tree_get_previous (tree))
2229 GString *prefix2 = g_string_new (prefix);
2232 g_string_append (prefix2, " │ ");
2234 g_string_append (prefix2, " ");
2235 for (i = 0; i < len; i++)
2236 g_string_append_c (prefix2, ' ');
2238 _gtk_css_selector_tree_print (gtk_css_selector_tree_get_previous (tree), str, prefix2->str);
2239 g_string_free (prefix2, TRUE);
2242 g_string_append (str, "\n");
2248 _gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
2251 const GtkCssSelectorTree *parent;
2253 g_return_if_fail (tree != NULL);
2255 tree->selector.class->print (&tree->selector, str);
2257 parent = gtk_css_selector_tree_get_parent (tree);
2259 _gtk_css_selector_tree_match_print (parent, str);
2263 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
2274 GtkCssSelector *current_selector;
2275 GtkCssSelectorTree **selector_match;
2276 } GtkCssSelectorRuleSetInfo;
2278 static GtkCssSelectorTree *
2279 get_tree (GByteArray *array, gint32 offset)
2281 return (GtkCssSelectorTree *) (array->data + offset);
2284 static GtkCssSelectorTree *
2285 alloc_tree (GByteArray *array, gint32 *offset)
2287 GtkCssSelectorTree tree = { { NULL} };
2289 *offset = array->len;
2290 g_byte_array_append (array, (guint8 *)&tree, sizeof (GtkCssSelectorTree));
2291 return get_tree (array, *offset);
2295 subdivide_infos (GByteArray *array, GList *infos, gint32 parent_offset)
2302 GtkCssSelectorTree *tree;
2303 GtkCssSelectorRuleSetInfo *info;
2304 GtkCssSelector max_selector;
2305 GHashTableIter iter;
2307 gpointer key, value;
2308 GPtrArray *exact_matches;
2312 return GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
2314 ht = gtk_css_selectors_count_initial_init ();
2316 for (l = infos; l != NULL; l = l->next)
2319 gtk_css_selectors_count_initial (info->current_selector, ht);
2322 /* Pick the selector with highest count, and use as decision on this level
2323 as that makes it possible to skip the largest amount of checks later */
2327 g_hash_table_iter_init (&iter, ht);
2328 while (g_hash_table_iter_next (&iter, &key, &value))
2330 GtkCssSelector *selector = key;
2331 if (GPOINTER_TO_UINT (value) > max_count ||
2332 (GPOINTER_TO_UINT (value) == max_count &&
2333 gtk_css_selector_compare_one (selector, &max_selector) < 0))
2335 max_count = GPOINTER_TO_UINT (value);
2336 max_selector = *selector;
2343 tree = alloc_tree (array, &tree_offset);
2344 tree->parent_offset = parent_offset;
2345 tree->selector = max_selector;
2347 exact_matches = NULL;
2348 for (l = infos; l != NULL; l = l->next)
2352 if (gtk_css_selectors_has_initial_selector (info->current_selector, &max_selector))
2354 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &max_selector);
2355 if (info->current_selector == NULL)
2357 /* Matches current node */
2358 if (exact_matches == NULL)
2359 exact_matches = g_ptr_array_new ();
2360 g_ptr_array_add (exact_matches, info->match);
2361 if (info->selector_match != NULL)
2362 *info->selector_match = GUINT_TO_POINTER (tree_offset);
2365 matched = g_list_prepend (matched, info);
2369 remaining = g_list_prepend (remaining, info);
2375 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
2377 g_byte_array_append (array, (guint8 *)exact_matches->pdata,
2378 exact_matches->len * sizeof (gpointer));
2379 g_ptr_array_free (exact_matches, TRUE);
2382 res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
2383 get_tree (array, tree_offset)->matches_offset = res;
2385 res = subdivide_infos (array, matched, tree_offset);
2386 get_tree (array, tree_offset)->previous_offset = res;
2388 res = subdivide_infos (array, remaining, parent_offset);
2389 get_tree (array, tree_offset)->sibling_offset = res;
2391 g_list_free (matched);
2392 g_list_free (remaining);
2393 g_hash_table_unref (ht);
2398 struct _GtkCssSelectorTreeBuilder {
2402 GtkCssSelectorTreeBuilder *
2403 _gtk_css_selector_tree_builder_new (void)
2405 return g_new0 (GtkCssSelectorTreeBuilder, 1);
2409 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
2411 g_list_free_full (builder->infos, g_free);
2416 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
2417 GtkCssSelector *selectors,
2418 GtkCssSelectorTree **selector_match,
2421 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
2423 info->match = match;
2424 info->current_selector = selectors;
2425 info->selector_match = selector_match;
2426 builder->infos = g_list_prepend (builder->infos, info);
2429 /* Convert all offsets to node-relative */
2431 fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
2433 while (tree != NULL)
2435 if (tree->parent_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2436 tree->parent_offset -= ((guint8 *)tree - data);
2438 if (tree->previous_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2439 tree->previous_offset -= ((guint8 *)tree - data);
2441 if (tree->sibling_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2442 tree->sibling_offset -= ((guint8 *)tree - data);
2444 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2445 tree->matches_offset -= ((guint8 *)tree - data);
2447 fixup_offsets ((GtkCssSelectorTree *)gtk_css_selector_tree_get_previous (tree), data);
2449 tree = (GtkCssSelectorTree *)gtk_css_selector_tree_get_sibling (tree);
2453 GtkCssSelectorTree *
2454 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
2456 GtkCssSelectorTree *tree;
2461 GtkCssSelectorRuleSetInfo *info;
2463 array = g_byte_array_new ();
2464 subdivide_infos (array, builder->infos, GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET);
2467 data = g_byte_array_free (array, FALSE);
2469 /* shrink to final size */
2470 data = g_realloc (data, len);
2472 tree = (GtkCssSelectorTree *)data;
2474 fixup_offsets (tree, data);
2476 /* Convert offsets to final pointers */
2477 for (l = builder->infos; l != NULL; l = l->next)
2480 if (info->selector_match)
2481 *info->selector_match = (GtkCssSelectorTree *)(data + GPOINTER_TO_UINT (*info->selector_match));
2486 GString *s = g_string_new ("");
2487 _gtk_css_selector_tree_print (tree, s, "");
2488 g_print ("%s", s->str);
2489 g_string_free (s, TRUE);