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_get_previous_change (const GtkCssSelectorTree *tree,
200 const GtkCssMatcher *matcher)
202 GtkCssChange previous_change = 0;
203 const GtkCssSelectorTree *prev;
205 for (prev = gtk_css_selector_tree_get_previous (tree);
207 prev = gtk_css_selector_tree_get_sibling (prev))
208 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
210 return previous_change;
216 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
219 g_string_append_c (string, ' ');
223 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
224 const GtkCssMatcher *matcher)
226 GtkCssMatcher ancestor;
228 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
232 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
240 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
241 const GtkCssMatcher *matcher,
244 GtkCssMatcher ancestor;
245 const GtkCssSelectorTree *prev;
247 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
251 for (prev = gtk_css_selector_tree_get_previous (tree);
253 prev = gtk_css_selector_tree_get_sibling (prev))
254 gtk_css_selector_tree_match (prev, matcher, res);
256 /* any matchers are dangerous here, as we may loop forever, but
257 we can terminate now as all possible matches have already been added */
258 if (_gtk_css_matcher_matches_any (matcher))
264 gtk_css_selector_descendant_tree_get_change (const GtkCssSelectorTree *tree,
265 const GtkCssMatcher *matcher)
267 GtkCssMatcher ancestor;
268 GtkCssChange change, previous_change;
272 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
276 previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
278 /* any matchers are dangerous here, as we may loop forever, but
279 we can terminate now as all possible matches have already been added */
280 if (_gtk_css_matcher_matches_any (matcher))
284 if (previous_change != 0)
285 change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
291 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
292 const GtkCssSelector *b)
298 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
300 return _gtk_css_change_for_child (previous_change);
303 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
305 gtk_css_selector_descendant_print,
306 gtk_css_selector_descendant_match,
307 gtk_css_selector_descendant_tree_match,
308 gtk_css_selector_descendant_get_change,
309 gtk_css_selector_descendant_tree_get_change,
310 gtk_css_selector_descendant_compare_one,
311 FALSE, FALSE, FALSE, FALSE, FALSE
317 gtk_css_selector_child_print (const GtkCssSelector *selector,
320 g_string_append (string, " > ");
324 gtk_css_selector_child_match (const GtkCssSelector *selector,
325 const GtkCssMatcher *matcher)
327 GtkCssMatcher parent;
329 if (!_gtk_css_matcher_get_parent (&parent, matcher))
332 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
336 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
337 const GtkCssMatcher *matcher,
340 GtkCssMatcher parent;
341 const GtkCssSelectorTree *prev;
343 if (!_gtk_css_matcher_get_parent (&parent, matcher))
346 for (prev = gtk_css_selector_tree_get_previous (tree);
348 prev = gtk_css_selector_tree_get_sibling (prev))
349 gtk_css_selector_tree_match (prev, &parent, res);
354 gtk_css_selector_child_tree_get_change (const GtkCssSelectorTree *tree,
355 const GtkCssMatcher *matcher)
357 GtkCssMatcher parent;
358 GtkCssChange change, previous_change;
360 if (!_gtk_css_matcher_get_parent (&parent, matcher))
365 previous_change = gtk_css_selector_tree_get_previous_change (tree, &parent);
367 if (previous_change != 0)
368 change |= _gtk_css_change_for_child (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
374 gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
376 return _gtk_css_change_for_child (previous_change);
380 gtk_css_selector_child_compare_one (const GtkCssSelector *a,
381 const GtkCssSelector *b)
386 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
388 gtk_css_selector_child_print,
389 gtk_css_selector_child_match,
390 gtk_css_selector_child_tree_match,
391 gtk_css_selector_child_get_change,
392 gtk_css_selector_child_tree_get_change,
393 gtk_css_selector_child_compare_one,
394 FALSE, FALSE, FALSE, FALSE, FALSE
400 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
403 g_string_append (string, " ~ ");
407 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
408 const GtkCssMatcher *matcher)
410 GtkCssMatcher previous;
412 while (_gtk_css_matcher_get_previous (&previous, matcher))
416 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
424 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
425 const GtkCssMatcher *matcher,
428 GtkCssMatcher previous;
429 const GtkCssSelectorTree *prev;
431 while (_gtk_css_matcher_get_previous (&previous, matcher))
435 for (prev = gtk_css_selector_tree_get_previous (tree);
437 prev = gtk_css_selector_tree_get_sibling (prev))
438 gtk_css_selector_tree_match (prev, matcher, res);
440 /* any matchers are dangerous here, as we may loop forever, but
441 we can terminate now as all possible matches have already been added */
442 if (_gtk_css_matcher_matches_any (matcher))
448 gtk_css_selector_sibling_tree_get_change (const GtkCssSelectorTree *tree,
449 const GtkCssMatcher *matcher)
451 GtkCssMatcher previous;
452 GtkCssChange change, previous_change;
457 while (_gtk_css_matcher_get_previous (&previous, matcher))
461 previous_change |= gtk_css_selector_tree_get_previous_change (tree, matcher);
463 /* any matchers are dangerous here, as we may loop forever, but
464 we can terminate now as all possible matches have already been added */
465 if (_gtk_css_matcher_matches_any (matcher))
469 if (previous_change != 0)
470 change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
476 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
478 return _gtk_css_change_for_sibling (previous_change);
482 gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
483 const GtkCssSelector *b)
489 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
491 gtk_css_selector_sibling_print,
492 gtk_css_selector_sibling_match,
493 gtk_css_selector_sibling_tree_match,
494 gtk_css_selector_sibling_get_change,
495 gtk_css_selector_sibling_tree_get_change,
496 gtk_css_selector_sibling_compare_one,
497 FALSE, FALSE, FALSE, FALSE, FALSE
503 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
506 g_string_append (string, " + ");
510 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
511 const GtkCssMatcher *matcher)
513 GtkCssMatcher previous;
515 if (!_gtk_css_matcher_get_previous (&previous, matcher))
518 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
522 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
523 const GtkCssMatcher *matcher,
526 GtkCssMatcher previous;
527 const GtkCssSelectorTree *prev;
529 if (!_gtk_css_matcher_get_previous (&previous, matcher))
534 for (prev = gtk_css_selector_tree_get_previous (tree);
536 prev = gtk_css_selector_tree_get_sibling (prev))
537 gtk_css_selector_tree_match (prev, matcher, res);
541 gtk_css_selector_adjacent_tree_get_change (const GtkCssSelectorTree *tree,
542 const GtkCssMatcher *matcher)
544 GtkCssMatcher previous;
545 GtkCssChange change, previous_change;
547 if (!_gtk_css_matcher_get_previous (&previous, matcher))
552 previous_change = gtk_css_selector_tree_get_previous_change (tree, &previous);
554 if (previous_change != 0)
555 change |= _gtk_css_change_for_sibling (previous_change) | GTK_CSS_CHANGE_GOT_MATCH;
561 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
563 return _gtk_css_change_for_sibling (previous_change);
567 gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
568 const GtkCssSelector *b)
573 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
575 gtk_css_selector_adjacent_print,
576 gtk_css_selector_adjacent_match,
577 gtk_css_selector_adjacent_tree_match,
578 gtk_css_selector_adjacent_get_change,
579 gtk_css_selector_adjacent_tree_get_change,
580 gtk_css_selector_adjacent_compare_one,
581 FALSE, FALSE, FALSE, FALSE, FALSE
587 gtk_css_selector_any_print (const GtkCssSelector *selector,
590 g_string_append_c (string, '*');
594 gtk_css_selector_any_match (const GtkCssSelector *selector,
595 const GtkCssMatcher *matcher)
597 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
600 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
601 _gtk_css_matcher_has_regions (matcher))
603 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
607 return gtk_css_selector_match (previous, matcher);
611 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
612 const GtkCssMatcher *matcher,
615 const GtkCssSelectorTree *prev, *prev2;
617 gtk_css_selector_tree_found_match (tree, res);
619 for (prev = gtk_css_selector_tree_get_previous (tree);
621 prev = gtk_css_selector_tree_get_sibling (prev))
623 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
624 _gtk_css_matcher_has_regions (matcher))
626 for (prev2 = gtk_css_selector_tree_get_previous (prev);
628 prev2 = gtk_css_selector_tree_get_sibling (prev2))
629 gtk_css_selector_tree_match (prev2, matcher, res);
632 gtk_css_selector_tree_match (prev, matcher, res);
637 gtk_css_selector_any_tree_get_change (const GtkCssSelectorTree *tree,
638 const GtkCssMatcher *matcher)
640 const GtkCssSelectorTree *prev;
641 GtkCssChange change, previous_change;
645 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
646 change |= GTK_CSS_CHANGE_GOT_MATCH;
649 for (prev = gtk_css_selector_tree_get_previous (tree);
651 prev = gtk_css_selector_tree_get_sibling (prev))
653 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
654 _gtk_css_matcher_has_regions (matcher))
655 previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
657 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
660 if (previous_change != 0)
661 change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
668 gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
670 return previous_change;
674 gtk_css_selector_any_compare_one (const GtkCssSelector *a,
675 const GtkCssSelector *b)
680 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
682 gtk_css_selector_any_print,
683 gtk_css_selector_any_match,
684 gtk_css_selector_any_tree_match,
685 gtk_css_selector_any_get_change,
686 gtk_css_selector_any_tree_get_change,
687 gtk_css_selector_any_compare_one,
688 FALSE, FALSE, FALSE, TRUE, TRUE
694 gtk_css_selector_name_print (const GtkCssSelector *selector,
697 g_string_append (string, selector->data);
701 gtk_css_selector_name_match (const GtkCssSelector *selector,
702 const GtkCssMatcher *matcher)
704 if (!_gtk_css_matcher_has_name (matcher, selector->data))
707 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
711 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
712 const GtkCssMatcher *matcher,
715 const GtkCssSelectorTree *prev;
717 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
720 gtk_css_selector_tree_found_match (tree, res);
722 for (prev = gtk_css_selector_tree_get_previous (tree);
724 prev = gtk_css_selector_tree_get_sibling (prev))
725 gtk_css_selector_tree_match (prev, matcher, res);
729 gtk_css_selector_name_tree_get_change (const GtkCssSelectorTree *tree,
730 const GtkCssMatcher *matcher)
732 GtkCssChange change, previous_change;
734 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
739 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
740 change |= GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
742 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
745 change |= previous_change | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_GOT_MATCH;
752 gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
754 return previous_change | GTK_CSS_CHANGE_NAME;
758 gtk_css_selector_name_compare_one (const GtkCssSelector *a,
759 const GtkCssSelector *b)
761 return strcmp (a->data, b->data);
764 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
766 gtk_css_selector_name_print,
767 gtk_css_selector_name_match,
768 gtk_css_selector_name_tree_match,
769 gtk_css_selector_name_get_change,
770 gtk_css_selector_name_tree_get_change,
771 gtk_css_selector_name_compare_one,
772 FALSE, FALSE, TRUE, TRUE, FALSE
778 gtk_css_selector_region_print (const GtkCssSelector *selector,
781 g_string_append (string, selector->data);
785 gtk_css_selector_region_match (const GtkCssSelector *selector,
786 const GtkCssMatcher *matcher)
788 const GtkCssSelector *previous;
790 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
793 previous = gtk_css_selector_previous (selector);
794 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
795 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
798 return gtk_css_selector_match (previous, matcher);
802 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
803 const GtkCssMatcher *matcher,
806 const GtkCssSelectorTree *prev, *prev2;
808 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
811 gtk_css_selector_tree_found_match (tree, res);
813 for (prev = gtk_css_selector_tree_get_previous (tree);
815 prev = gtk_css_selector_tree_get_sibling (prev))
817 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
819 for (prev2 = gtk_css_selector_tree_get_previous (prev);
821 prev2 = gtk_css_selector_tree_get_sibling (prev2))
822 gtk_css_selector_tree_match (prev2, matcher, res);
825 gtk_css_selector_tree_match (prev, matcher, res);
830 gtk_css_selector_region_tree_get_change (const GtkCssSelectorTree *tree,
831 const GtkCssMatcher *matcher)
833 const GtkCssSelectorTree *prev;
834 GtkCssChange change, previous_change;
836 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
841 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
842 change |= GTK_CSS_CHANGE_REGION | GTK_CSS_CHANGE_GOT_MATCH;
845 for (prev = gtk_css_selector_tree_get_previous (tree);
847 prev = gtk_css_selector_tree_get_sibling (prev))
849 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
850 previous_change |= gtk_css_selector_tree_get_previous_change (prev, matcher);
852 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
855 if (previous_change != 0)
857 previous_change |= GTK_CSS_CHANGE_REGION;
858 previous_change |= _gtk_css_change_for_child (previous_change);
859 change |= previous_change | GTK_CSS_CHANGE_GOT_MATCH;
866 gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
870 change = previous_change;
871 change |= GTK_CSS_CHANGE_REGION;
872 change |= _gtk_css_change_for_child (change);
878 gtk_css_selector_region_compare_one (const GtkCssSelector *a,
879 const GtkCssSelector *b)
881 return strcmp (a->data, b->data);
884 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
886 gtk_css_selector_region_print,
887 gtk_css_selector_region_match,
888 gtk_css_selector_region_tree_match,
889 gtk_css_selector_region_get_change,
890 gtk_css_selector_region_tree_get_change,
891 gtk_css_selector_region_compare_one,
892 FALSE, FALSE, TRUE, TRUE, TRUE
898 gtk_css_selector_class_print (const GtkCssSelector *selector,
901 g_string_append_c (string, '.');
902 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
906 gtk_css_selector_class_match (const GtkCssSelector *selector,
907 const GtkCssMatcher *matcher)
909 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
912 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
916 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
917 const GtkCssMatcher *matcher,
920 const GtkCssSelectorTree *prev;
922 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
925 gtk_css_selector_tree_found_match (tree, res);
927 for (prev = gtk_css_selector_tree_get_previous (tree);
929 prev = gtk_css_selector_tree_get_sibling (prev))
930 gtk_css_selector_tree_match (prev, matcher, res);
934 gtk_css_selector_class_tree_get_change (const GtkCssSelectorTree *tree,
935 const GtkCssMatcher *matcher)
937 GtkCssChange change, previous_change;
939 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
944 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
945 change |= GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
947 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
949 if (previous_change != 0)
950 change |= previous_change | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_GOT_MATCH;
956 gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
958 return previous_change | GTK_CSS_CHANGE_CLASS;
962 gtk_css_selector_class_compare_one (const GtkCssSelector *a,
963 const GtkCssSelector *b)
965 return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
966 g_quark_to_string (GPOINTER_TO_UINT (b->data)));
969 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
971 gtk_css_selector_class_print,
972 gtk_css_selector_class_match,
973 gtk_css_selector_class_tree_match,
974 gtk_css_selector_class_get_change,
975 gtk_css_selector_class_tree_get_change,
976 gtk_css_selector_class_compare_one,
977 FALSE, TRUE, FALSE, TRUE, FALSE
983 gtk_css_selector_id_print (const GtkCssSelector *selector,
986 g_string_append_c (string, '#');
987 g_string_append (string, selector->data);
991 gtk_css_selector_id_match (const GtkCssSelector *selector,
992 const GtkCssMatcher *matcher)
994 if (!_gtk_css_matcher_has_id (matcher, selector->data))
997 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
1001 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
1002 const GtkCssMatcher *matcher,
1005 const GtkCssSelectorTree *prev;
1007 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
1010 gtk_css_selector_tree_found_match (tree, res);
1012 for (prev = gtk_css_selector_tree_get_previous (tree);
1014 prev = gtk_css_selector_tree_get_sibling (prev))
1015 gtk_css_selector_tree_match (prev, matcher, res);
1019 gtk_css_selector_id_tree_get_change (const GtkCssSelectorTree *tree,
1020 const GtkCssMatcher *matcher)
1022 GtkCssChange change, previous_change;
1024 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
1029 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1030 change |= GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
1032 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
1034 if (previous_change != 0)
1035 change |= previous_change | GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_GOT_MATCH;
1041 gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1043 return previous_change | GTK_CSS_CHANGE_ID;
1048 gtk_css_selector_id_compare_one (const GtkCssSelector *a,
1049 const GtkCssSelector *b)
1051 return strcmp (a->data, b->data);
1054 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
1056 gtk_css_selector_id_print,
1057 gtk_css_selector_id_match,
1058 gtk_css_selector_id_tree_match,
1059 gtk_css_selector_id_get_change,
1060 gtk_css_selector_id_tree_get_change,
1061 gtk_css_selector_id_compare_one,
1062 TRUE, FALSE, FALSE, TRUE, FALSE
1065 /* PSEUDOCLASS FOR STATE */
1068 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
1071 static const char * state_names[] = {
1082 state = GPOINTER_TO_UINT (selector->data);
1083 g_string_append_c (string, ':');
1085 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
1087 if (state == (1 << i))
1089 g_string_append (string, state_names[i]);
1094 g_assert_not_reached ();
1098 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
1099 const GtkCssMatcher *matcher)
1101 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
1103 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
1106 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
1110 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
1111 const GtkCssMatcher *matcher,
1114 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
1115 const GtkCssSelectorTree *prev;
1117 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
1120 gtk_css_selector_tree_found_match (tree, res);
1122 for (prev = gtk_css_selector_tree_get_previous (tree);
1124 prev = gtk_css_selector_tree_get_sibling (prev))
1125 gtk_css_selector_tree_match (prev, matcher, res);
1129 gtk_css_selector_pseudoclass_state_tree_get_change (const GtkCssSelectorTree *tree,
1130 const GtkCssMatcher *matcher)
1132 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
1133 GtkCssChange change, previous_change;
1135 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
1140 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1141 change |= GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
1143 previous_change = gtk_css_selector_tree_get_previous_change (tree, matcher);
1145 if (previous_change != 0)
1146 change |= previous_change | GTK_CSS_CHANGE_STATE | GTK_CSS_CHANGE_GOT_MATCH;
1152 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1154 return previous_change | GTK_CSS_CHANGE_STATE;
1158 gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
1159 const GtkCssSelector *b)
1161 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1164 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
1165 "pseudoclass-state",
1166 gtk_css_selector_pseudoclass_state_print,
1167 gtk_css_selector_pseudoclass_state_match,
1168 gtk_css_selector_pseudoclass_state_tree_match,
1169 gtk_css_selector_pseudoclass_state_get_change,
1170 gtk_css_selector_pseudoclass_state_tree_get_change,
1171 gtk_css_selector_pseudoclass_state_compare_one,
1172 FALSE, TRUE, FALSE, TRUE, FALSE
1175 /* PSEUDOCLASS FOR POSITION */
1183 #define POSITION_TYPE_BITS 2
1184 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
1186 static gconstpointer
1187 encode_position (PositionType type,
1194 gssize type :POSITION_TYPE_BITS;
1195 gssize a :POSITION_NUMBER_BITS;
1196 gssize b :POSITION_NUMBER_BITS;
1199 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
1201 g_assert (type < (1 << POSITION_TYPE_BITS));
1203 result.data.type = type;
1211 decode_position (const GtkCssSelector *selector,
1219 gssize type :POSITION_TYPE_BITS;
1220 gssize a :POSITION_NUMBER_BITS;
1221 gssize b :POSITION_NUMBER_BITS;
1224 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
1226 result.p = selector->data;
1228 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
1234 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
1240 decode_position (selector, &type, &a, &b);
1243 case POSITION_FORWARD:
1247 g_string_append (string, ":first-child");
1249 g_string_append_printf (string, ":nth-child(%d)", b);
1251 else if (a == 2 && b == 0)
1252 g_string_append (string, ":nth-child(even)");
1253 else if (a == 2 && b == 1)
1254 g_string_append (string, ":nth-child(odd)");
1257 g_string_append (string, ":nth-child(");
1259 g_string_append (string, "n");
1261 g_string_append (string, "-n");
1263 g_string_append_printf (string, "%dn", a);
1265 g_string_append_printf (string, "+%d)", b);
1267 g_string_append_printf (string, "%d)", b);
1269 g_string_append (string, ")");
1272 case POSITION_BACKWARD:
1276 g_string_append (string, ":last-child");
1278 g_string_append_printf (string, ":nth-last-child(%d)", b);
1280 else if (a == 2 && b == 0)
1281 g_string_append (string, ":nth-last-child(even)");
1282 else if (a == 2 && b == 1)
1283 g_string_append (string, ":nth-last-child(odd)");
1286 g_string_append (string, ":nth-last-child(");
1288 g_string_append (string, "n");
1290 g_string_append (string, "-n");
1292 g_string_append_printf (string, "%dn", a);
1294 g_string_append_printf (string, "+%d)", b);
1296 g_string_append_printf (string, "%d)", b);
1298 g_string_append (string, ")");
1302 g_string_append (string, ":only-child");
1304 case POSITION_SORTED:
1305 g_string_append (string, ":sorted");
1308 g_assert_not_reached ();
1314 get_selector_flags_for_position_region_match (const GtkCssSelector *selector, GtkRegionFlags *selector_flags)
1319 decode_position (selector, &type, &a, &b);
1322 case POSITION_FORWARD:
1323 if (a == 0 && b == 1)
1324 *selector_flags = GTK_REGION_FIRST;
1325 else if (a == 2 && b == 0)
1326 *selector_flags = GTK_REGION_EVEN;
1327 else if (a == 2 && b == 1)
1328 *selector_flags = GTK_REGION_ODD;
1332 case POSITION_BACKWARD:
1333 if (a == 0 && b == 1)
1334 *selector_flags = GTK_REGION_LAST;
1339 *selector_flags = GTK_REGION_ONLY;
1341 case POSITION_SORTED:
1342 *selector_flags = GTK_REGION_SORTED;
1345 g_assert_not_reached ();
1353 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
1354 const GtkCssMatcher *matcher)
1356 GtkRegionFlags selector_flags;
1357 const GtkCssSelector *previous;
1359 if (!get_selector_flags_for_position_region_match (selector, &selector_flags))
1362 selector = gtk_css_selector_previous (selector);
1364 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
1367 previous = gtk_css_selector_previous (selector);
1368 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
1369 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
1372 return gtk_css_selector_match (previous, matcher);
1376 get_position_match (const GtkCssSelector *selector,
1377 const GtkCssMatcher *matcher)
1382 decode_position (selector, &type, &a, &b);
1385 case POSITION_FORWARD:
1386 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1389 case POSITION_BACKWARD:
1390 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1394 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1395 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1398 case POSITION_SORTED:
1401 g_assert_not_reached ();
1409 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
1410 const GtkCssMatcher *matcher)
1412 const GtkCssSelector *previous;
1414 previous = gtk_css_selector_previous (selector);
1415 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
1416 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
1418 if (!get_position_match (selector, matcher))
1421 return gtk_css_selector_match (previous, matcher);
1425 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
1426 const GtkCssSelectorTree *prev,
1427 const GtkCssMatcher *matcher,
1430 const GtkCssSelectorTree *prev2;
1431 GtkRegionFlags selector_flags;
1433 if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
1436 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1439 gtk_css_selector_tree_found_match (prev, res);
1441 for (prev2 = gtk_css_selector_tree_get_previous (prev);
1443 prev2 = gtk_css_selector_tree_get_sibling (prev2))
1445 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1446 gtk_css_selector_tree_match (gtk_css_selector_tree_get_previous (prev2), matcher, res);
1447 gtk_css_selector_tree_match (prev2, matcher, res);
1452 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1453 const GtkCssMatcher *matcher,
1456 const GtkCssSelectorTree *prev;
1458 for (prev = gtk_css_selector_tree_get_previous (tree);
1460 prev = gtk_css_selector_tree_get_sibling (prev))
1462 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1463 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1466 if (!get_position_match (&tree->selector, matcher))
1469 gtk_css_selector_tree_found_match (tree, res);
1471 for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
1473 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1474 gtk_css_selector_tree_match (prev, matcher, res);
1479 gtk_css_selector_pseudoclass_position_tree_get_change_for_region (const GtkCssSelectorTree *tree,
1480 const GtkCssSelectorTree *prev,
1481 const GtkCssMatcher *matcher)
1483 const GtkCssSelectorTree *prev2;
1484 GtkRegionFlags selector_flags;
1485 GtkCssChange change, previous_change;
1487 if (!get_selector_flags_for_position_region_match (&tree->selector, &selector_flags))
1490 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1494 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1495 change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1497 previous_change = 0;
1498 for (prev2 = gtk_css_selector_tree_get_previous (prev);
1500 prev2 = gtk_css_selector_tree_get_sibling (prev2))
1502 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1503 previous_change |= gtk_css_selector_tree_get_change (gtk_css_selector_tree_get_previous (prev2), matcher);
1504 previous_change |= gtk_css_selector_tree_get_change (prev2, matcher);
1507 if (previous_change != 0)
1508 change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1514 gtk_css_selector_pseudoclass_position_tree_get_change (const GtkCssSelectorTree *tree,
1515 const GtkCssMatcher *matcher)
1517 const GtkCssSelectorTree *prev;
1518 GtkCssChange change, previous_change;
1522 for (prev = gtk_css_selector_tree_get_previous (tree);
1524 prev = gtk_css_selector_tree_get_sibling (prev))
1526 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1527 change |= gtk_css_selector_pseudoclass_position_tree_get_change_for_region (tree, prev, matcher);
1530 if (!get_position_match (&tree->selector, matcher))
1533 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
1534 change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1536 previous_change = 0;
1537 for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
1539 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1540 previous_change |= gtk_css_selector_tree_get_change (prev, matcher);
1543 if (previous_change != 0)
1544 change |= previous_change | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_GOT_MATCH;
1550 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1552 return previous_change | GTK_CSS_CHANGE_POSITION;
1556 gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
1557 const GtkCssSelector *b)
1559 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1562 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1563 "pseudoclass-position",
1564 gtk_css_selector_pseudoclass_position_print,
1565 gtk_css_selector_pseudoclass_position_match,
1566 gtk_css_selector_pseudoclass_position_tree_match,
1567 gtk_css_selector_pseudoclass_position_get_change,
1568 gtk_css_selector_pseudoclass_position_tree_get_change,
1569 gtk_css_selector_pseudoclass_position_compare_one,
1570 FALSE, TRUE, FALSE, TRUE, TRUE
1576 gtk_css_selector_size (const GtkCssSelector *selector)
1582 selector = gtk_css_selector_previous (selector);
1589 static GtkCssSelector *
1590 gtk_css_selector_new (const GtkCssSelectorClass *class,
1591 GtkCssSelector *selector,
1596 size = gtk_css_selector_size (selector);
1597 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1599 selector[1].class = NULL;
1601 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1603 selector->class = class;
1604 selector->data = data;
1609 static GtkCssSelector *
1610 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1614 name = _gtk_css_parser_try_name (parser, FALSE);
1618 _gtk_css_parser_error (parser, "Expected a valid name for class");
1620 _gtk_css_selector_free (selector);
1624 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1626 GUINT_TO_POINTER (g_quark_from_string (name)));
1633 static GtkCssSelector *
1634 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1638 name = _gtk_css_parser_try_name (parser, FALSE);
1642 _gtk_css_parser_error (parser, "Expected a valid name for id");
1644 _gtk_css_selector_free (selector);
1648 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1650 g_intern_string (name));
1657 static GtkCssSelector *
1658 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1659 GtkCssSelector *selector,
1664 if (!_gtk_css_parser_try (parser, "(", TRUE))
1666 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1668 _gtk_css_selector_free (selector);
1672 if (_gtk_css_parser_try (parser, "even", TRUE))
1677 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1682 else if (type == POSITION_FORWARD &&
1683 _gtk_css_parser_try (parser, "first", TRUE))
1688 else if (type == POSITION_FORWARD &&
1689 _gtk_css_parser_try (parser, "last", TRUE))
1693 type = POSITION_BACKWARD;
1699 if (_gtk_css_parser_try (parser, "+", TRUE))
1701 else if (_gtk_css_parser_try (parser, "-", TRUE))
1706 if (_gtk_css_parser_try_int (parser, &a))
1710 _gtk_css_parser_error (parser, "Expected an integer");
1712 _gtk_css_selector_free (selector);
1717 else if (_gtk_css_parser_has_prefix (parser, "n"))
1723 _gtk_css_parser_error (parser, "Expected an integer");
1725 _gtk_css_selector_free (selector);
1729 if (_gtk_css_parser_try (parser, "n", TRUE))
1731 if (_gtk_css_parser_try (parser, "+", TRUE))
1733 else if (_gtk_css_parser_try (parser, "-", TRUE))
1738 if (_gtk_css_parser_try_int (parser, &b))
1742 _gtk_css_parser_error (parser, "Expected an integer");
1744 _gtk_css_selector_free (selector);
1760 if (!_gtk_css_parser_try (parser, ")", FALSE))
1762 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1764 _gtk_css_selector_free (selector);
1768 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1770 encode_position (type, a, b));
1775 static GtkCssSelector *
1776 parse_selector_pseudo_class (GtkCssParser *parser,
1777 GtkCssSelector *selector)
1779 static const struct {
1781 GtkStateFlags state_flag;
1782 PositionType position_type;
1785 } pseudo_classes[] = {
1786 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1787 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1788 { "only-child", 0, POSITION_ONLY, 0, 0 },
1789 { "sorted", 0, POSITION_SORTED, 0, 0 },
1790 { "active", GTK_STATE_FLAG_ACTIVE, },
1791 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1792 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1793 { "selected", GTK_STATE_FLAG_SELECTED, },
1794 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1795 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1796 { "focused", GTK_STATE_FLAG_FOCUSED, },
1797 { "focus", GTK_STATE_FLAG_FOCUSED, },
1798 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
1802 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1803 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1804 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1805 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1807 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1809 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1811 if (pseudo_classes[i].state_flag)
1812 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1814 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1816 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1818 encode_position (pseudo_classes[i].position_type,
1819 pseudo_classes[i].position_a,
1820 pseudo_classes[i].position_b));
1825 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1827 _gtk_css_selector_free (selector);
1831 static GtkCssSelector *
1832 try_parse_name (GtkCssParser *parser,
1833 GtkCssSelector *selector)
1837 name = _gtk_css_parser_try_ident (parser, FALSE);
1840 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
1841 ? >K_CSS_SELECTOR_REGION
1842 : >K_CSS_SELECTOR_NAME,
1844 g_intern_string (name));
1847 else if (_gtk_css_parser_try (parser, "*", FALSE))
1848 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1853 static GtkCssSelector *
1854 parse_simple_selector (GtkCssParser *parser,
1855 GtkCssSelector *selector)
1857 guint size = gtk_css_selector_size (selector);
1859 selector = try_parse_name (parser, selector);
1862 if (_gtk_css_parser_try (parser, "#", FALSE))
1863 selector = parse_selector_id (parser, selector);
1864 else if (_gtk_css_parser_try (parser, ".", FALSE))
1865 selector = parse_selector_class (parser, selector);
1866 else if (_gtk_css_parser_try (parser, ":", FALSE))
1867 selector = parse_selector_pseudo_class (parser, selector);
1868 else if (gtk_css_selector_size (selector) == size)
1870 _gtk_css_parser_error (parser, "Expected a valid selector");
1872 _gtk_css_selector_free (selector);
1878 while (selector && !_gtk_css_parser_is_eof (parser));
1880 _gtk_css_parser_skip_whitespace (parser);
1886 _gtk_css_selector_parse (GtkCssParser *parser)
1888 GtkCssSelector *selector = NULL;
1890 while ((selector = parse_simple_selector (parser, selector)) &&
1891 !_gtk_css_parser_is_eof (parser) &&
1892 !_gtk_css_parser_begins_with (parser, ',') &&
1893 !_gtk_css_parser_begins_with (parser, '{'))
1895 if (_gtk_css_parser_try (parser, "+", TRUE))
1896 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1897 else if (_gtk_css_parser_try (parser, "~", TRUE))
1898 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1899 else if (_gtk_css_parser_try (parser, ">", TRUE))
1900 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1902 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1909 _gtk_css_selector_free (GtkCssSelector *selector)
1911 g_return_if_fail (selector != NULL);
1917 _gtk_css_selector_print (const GtkCssSelector *selector,
1920 const GtkCssSelector *previous;
1922 g_return_if_fail (selector != NULL);
1924 previous = gtk_css_selector_previous (selector);
1926 _gtk_css_selector_print (previous, str);
1928 selector->class->print (selector, str);
1932 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1936 g_return_val_if_fail (selector != NULL, NULL);
1938 string = g_string_new (NULL);
1940 _gtk_css_selector_print (selector, string);
1942 return g_string_free (string, FALSE);
1947 _gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree)
1949 GtkCssChange change = 0;
1953 change = tree->selector.class->get_change (&tree->selector, change);
1954 tree = gtk_css_selector_tree_get_parent (tree);
1961 * _gtk_css_selector_matches:
1962 * @selector: the selector
1963 * @path: the path to check
1964 * @state: The state to match
1966 * Checks if the @selector matches the given @path. If @length is
1967 * smaller than the number of elements in @path, it is assumed that
1968 * only the first @length element of @path are valid and the rest
1969 * does not exist. This is useful for doing parent matches for the
1970 * 'inherit' keyword.
1972 * Returns: %TRUE if the selector matches @path
1975 _gtk_css_selector_matches (const GtkCssSelector *selector,
1976 const GtkCssMatcher *matcher)
1979 g_return_val_if_fail (selector != NULL, FALSE);
1980 g_return_val_if_fail (matcher != NULL, FALSE);
1982 return gtk_css_selector_match (selector, matcher);
1985 /* Computes specificity according to CSS 2.1.
1986 * The arguments must be initialized to 0 */
1988 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1993 for (; selector; selector = gtk_css_selector_previous (selector))
1995 const GtkCssSelectorClass *klass = selector->class;
1997 if (klass->increase_id_specificity)
1999 if (klass->increase_class_specificity)
2001 if (klass->increase_element_specificity)
2007 _gtk_css_selector_compare (const GtkCssSelector *a,
2008 const GtkCssSelector *b)
2010 guint a_ids = 0, a_classes = 0, a_elements = 0;
2011 guint b_ids = 0, b_classes = 0, b_elements = 0;
2014 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
2015 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
2017 compare = a_ids - b_ids;
2021 compare = a_classes - b_classes;
2025 return a_elements - b_elements;
2029 /******************** SelectorTree handling *****************/
2032 gtk_css_selectors_count_initial_init (void)
2034 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
2038 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
2040 if (!selector->class->is_simple || selector->class->must_keep_order)
2042 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
2043 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
2048 selector && selector->class->is_simple && !selector->class->must_keep_order;
2049 selector = gtk_css_selector_previous (selector))
2051 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
2052 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
2057 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
2059 if (!selector->class->is_simple || selector->class->must_keep_order)
2060 return gtk_css_selector_equal (selector, initial);
2063 selector && selector->class->is_simple && !selector->class->must_keep_order;
2064 selector = gtk_css_selector_previous (selector))
2066 if (gtk_css_selector_equal (selector, initial))
2073 static GtkCssSelector *
2074 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
2076 GtkCssSelector *found;
2079 /* If the initial simple selector is not first, move it there so we can skip it
2080 without losing any other selectors */
2081 if (!gtk_css_selector_equal (selector, initial))
2083 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
2085 if (gtk_css_selector_equal (found, initial))
2089 g_assert (found != NULL && found->class->is_simple);
2096 return (GtkCssSelector *)gtk_css_selector_previous (selector);
2100 direct_ptr_compare (const void *_a, const void *_b)
2102 gpointer *a = (gpointer *)_a;
2103 gpointer *b = (gpointer *)_b;
2112 _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
2113 const GtkCssMatcher *matcher)
2117 GHashTableIter iter;
2120 res = g_hash_table_new (g_direct_hash, g_direct_equal);
2122 for (; tree != NULL;
2123 tree = gtk_css_selector_tree_get_sibling (tree))
2124 gtk_css_selector_tree_match (tree, matcher, res);
2126 array = g_ptr_array_sized_new (g_hash_table_size (res));
2128 g_hash_table_iter_init (&iter, res);
2129 while (g_hash_table_iter_next (&iter, &key, NULL))
2130 g_ptr_array_add (array, key);
2132 g_hash_table_destroy (res);
2134 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
2140 _gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
2141 const GtkCssMatcher *matcher)
2143 GtkCssChange change;
2147 for (; tree != NULL;
2148 tree = gtk_css_selector_tree_get_sibling (tree))
2149 change |= gtk_css_selector_tree_get_change (tree, matcher);
2151 /* Never return reserved bit set */
2152 return change & ~GTK_CSS_CHANGE_RESERVED_BIT;
2157 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
2159 gboolean first = TRUE;
2162 for (; tree != NULL; tree = tree->siblings, first = FALSE)
2165 g_string_append (str, prefix);
2170 g_string_append (str, "─┬─");
2172 g_string_append (str, "───");
2177 g_string_append (str, " ├─");
2179 g_string_append (str, " └─");
2183 tree->selector.class->print (&tree->selector, str);
2184 len = str->len - len;
2186 if (gtk_css_selector_tree_get_previous (tree))
2188 GString *prefix2 = g_string_new (prefix);
2191 g_string_append (prefix2, " │ ");
2193 g_string_append (prefix2, " ");
2194 for (i = 0; i < len; i++)
2195 g_string_append_c (prefix2, ' ');
2197 _gtk_css_selector_tree_print (gtk_css_selector_tree_get_previous (tree), str, prefix2->str);
2198 g_string_free (prefix2, TRUE);
2201 g_string_append (str, "\n");
2207 _gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
2210 const GtkCssSelectorTree *parent;
2212 g_return_if_fail (tree != NULL);
2214 tree->selector.class->print (&tree->selector, str);
2216 parent = gtk_css_selector_tree_get_parent (tree);
2218 _gtk_css_selector_tree_match_print (parent, str);
2222 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
2233 GtkCssSelector *current_selector;
2234 GtkCssSelectorTree **selector_match;
2235 } GtkCssSelectorRuleSetInfo;
2237 static GtkCssSelectorTree *
2238 get_tree (GByteArray *array, gint32 offset)
2240 return (GtkCssSelectorTree *) (array->data + offset);
2243 static GtkCssSelectorTree *
2244 alloc_tree (GByteArray *array, gint32 *offset)
2246 GtkCssSelectorTree tree = { { NULL} };
2248 *offset = array->len;
2249 g_byte_array_append (array, (guint8 *)&tree, sizeof (GtkCssSelectorTree));
2250 return get_tree (array, *offset);
2254 subdivide_infos (GByteArray *array, GList *infos, gint32 parent_offset)
2261 GtkCssSelectorTree *tree;
2262 GtkCssSelectorRuleSetInfo *info;
2263 GtkCssSelector max_selector;
2264 GHashTableIter iter;
2266 gpointer key, value;
2267 GPtrArray *exact_matches;
2271 return GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
2273 ht = gtk_css_selectors_count_initial_init ();
2275 for (l = infos; l != NULL; l = l->next)
2278 gtk_css_selectors_count_initial (info->current_selector, ht);
2281 /* Pick the selector with highest count, and use as decision on this level
2282 as that makes it possible to skip the largest amount of checks later */
2286 g_hash_table_iter_init (&iter, ht);
2287 while (g_hash_table_iter_next (&iter, &key, &value))
2289 GtkCssSelector *selector = key;
2290 if (GPOINTER_TO_UINT (value) > max_count ||
2291 (GPOINTER_TO_UINT (value) == max_count &&
2292 gtk_css_selector_compare_one (selector, &max_selector) < 0))
2294 max_count = GPOINTER_TO_UINT (value);
2295 max_selector = *selector;
2302 tree = alloc_tree (array, &tree_offset);
2303 tree->parent_offset = parent_offset;
2304 tree->selector = max_selector;
2306 exact_matches = NULL;
2307 for (l = infos; l != NULL; l = l->next)
2311 if (gtk_css_selectors_has_initial_selector (info->current_selector, &max_selector))
2313 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &max_selector);
2314 if (info->current_selector == NULL)
2316 /* Matches current node */
2317 if (exact_matches == NULL)
2318 exact_matches = g_ptr_array_new ();
2319 g_ptr_array_add (exact_matches, info->match);
2320 if (info->selector_match != NULL)
2321 *info->selector_match = GUINT_TO_POINTER (tree_offset);
2324 matched = g_list_prepend (matched, info);
2328 remaining = g_list_prepend (remaining, info);
2334 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
2336 g_byte_array_append (array, (guint8 *)exact_matches->pdata,
2337 exact_matches->len * sizeof (gpointer));
2338 g_ptr_array_free (exact_matches, TRUE);
2341 res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
2342 get_tree (array, tree_offset)->matches_offset = res;
2344 res = subdivide_infos (array, matched, tree_offset);
2345 get_tree (array, tree_offset)->previous_offset = res;
2347 res = subdivide_infos (array, remaining, parent_offset);
2348 get_tree (array, tree_offset)->sibling_offset = res;
2350 g_list_free (matched);
2351 g_list_free (remaining);
2352 g_hash_table_unref (ht);
2357 struct _GtkCssSelectorTreeBuilder {
2361 GtkCssSelectorTreeBuilder *
2362 _gtk_css_selector_tree_builder_new (void)
2364 return g_new0 (GtkCssSelectorTreeBuilder, 1);
2368 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
2370 g_list_free_full (builder->infos, g_free);
2375 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
2376 GtkCssSelector *selectors,
2377 GtkCssSelectorTree **selector_match,
2380 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
2382 info->match = match;
2383 info->current_selector = selectors;
2384 info->selector_match = selector_match;
2385 builder->infos = g_list_prepend (builder->infos, info);
2388 /* Convert all offsets to node-relative */
2390 fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
2392 while (tree != NULL)
2394 if (tree->parent_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2395 tree->parent_offset -= ((guint8 *)tree - data);
2397 if (tree->previous_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2398 tree->previous_offset -= ((guint8 *)tree - data);
2400 if (tree->sibling_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2401 tree->sibling_offset -= ((guint8 *)tree - data);
2403 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2404 tree->matches_offset -= ((guint8 *)tree - data);
2406 fixup_offsets ((GtkCssSelectorTree *)gtk_css_selector_tree_get_previous (tree), data);
2408 tree = (GtkCssSelectorTree *)gtk_css_selector_tree_get_sibling (tree);
2412 GtkCssSelectorTree *
2413 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
2415 GtkCssSelectorTree *tree;
2420 GtkCssSelectorRuleSetInfo *info;
2422 array = g_byte_array_new ();
2423 subdivide_infos (array, builder->infos, GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET);
2426 data = g_byte_array_free (array, FALSE);
2428 /* shrink to final size */
2429 data = g_realloc (data, len);
2431 tree = (GtkCssSelectorTree *)data;
2433 fixup_offsets (tree, data);
2435 /* Convert offsets to final pointers */
2436 for (l = builder->infos; l != NULL; l = l->next)
2439 if (info->selector_match)
2440 *info->selector_match = (GtkCssSelectorTree *)(data + GPOINTER_TO_UINT (*info->selector_match));
2445 GString *s = g_string_new ("");
2446 _gtk_css_selector_tree_print (tree, s, "");
2447 g_print ("%s", s->str);
2448 g_string_free (s, TRUE);