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 typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
30 struct _GtkCssSelectorClass {
33 void (* print) (const GtkCssSelector *selector,
35 gboolean (* match) (const GtkCssSelector *selector,
36 const GtkCssMatcher *matcher);
37 void (* tree_match) (const GtkCssSelectorTree *tree,
38 const GtkCssMatcher *matcher,
40 GtkCssChange (* get_change) (const GtkCssSelector *selector);
42 guint increase_id_specificity :1;
43 guint increase_class_specificity :1;
44 guint increase_element_specificity :1;
46 guint must_keep_order :1; /* Due to region weirdness these must be kept before a DESCENDANT, so don't reorder */
49 struct _GtkCssSelector
51 const GtkCssSelectorClass *class; /* type of check this selector does */
52 gconstpointer data; /* data for matching:
53 - interned string for CLASS, NAME and ID
54 - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
57 struct _GtkCssSelectorTree
59 GtkCssSelector selector;
60 GtkCssSelectorTree *parent;
61 GtkCssSelectorTree *previous;
62 GtkCssSelectorTree *siblings;
63 gpointer *matches; /* pointers that we return as matches if selector matches */
67 gtk_css_selector_equal (const GtkCssSelector *a,
68 const GtkCssSelector *b)
71 a->class == b->class &&
76 gtk_css_selector_hash (const GtkCssSelector *selector)
78 return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
82 gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
89 for (i = 0; tree->matches[i] != NULL; i++)
90 g_hash_table_insert (res, tree->matches[i], tree->matches[i]);
95 gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
96 const GtkCssMatcher *matcher,
102 tree->selector.class->tree_match (tree, matcher, res);
106 gtk_css_selector_match (const GtkCssSelector *selector,
107 const GtkCssMatcher *matcher)
109 if (selector == NULL)
112 return selector->class->match (selector, matcher);
116 gtk_css_selector_get_change (const GtkCssSelector *selector)
118 if (selector == NULL)
121 return selector->class->get_change (selector);
124 static const GtkCssSelector *
125 gtk_css_selector_previous (const GtkCssSelector *selector)
127 selector = selector + 1;
129 return selector->class ? selector : NULL;
135 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
138 g_string_append_c (string, ' ');
142 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
143 const GtkCssMatcher *matcher)
145 GtkCssMatcher ancestor;
147 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
151 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
159 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
160 const GtkCssMatcher *matcher,
163 GtkCssMatcher ancestor;
164 const GtkCssSelectorTree *prev;
166 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
170 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
171 gtk_css_selector_tree_match (prev, matcher, res);
173 /* any matchers are dangerous here, as we may loop forever, but
174 we can terminate now as all possible matches have already been added */
175 if (_gtk_css_matcher_matches_any (matcher))
181 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector)
183 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
186 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
188 gtk_css_selector_descendant_print,
189 gtk_css_selector_descendant_match,
190 gtk_css_selector_descendant_tree_match,
191 gtk_css_selector_descendant_get_change,
192 FALSE, FALSE, FALSE, FALSE, FALSE
198 gtk_css_selector_child_print (const GtkCssSelector *selector,
201 g_string_append (string, " > ");
205 gtk_css_selector_child_match (const GtkCssSelector *selector,
206 const GtkCssMatcher *matcher)
208 GtkCssMatcher parent;
210 if (!_gtk_css_matcher_get_parent (&parent, matcher))
213 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
217 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
218 const GtkCssMatcher *matcher,
221 GtkCssMatcher parent;
222 const GtkCssSelectorTree *prev;
224 if (!_gtk_css_matcher_get_parent (&parent, matcher))
227 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
228 gtk_css_selector_tree_match (prev, &parent, res);
232 gtk_css_selector_child_get_change (const GtkCssSelector *selector)
234 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
237 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
239 gtk_css_selector_child_print,
240 gtk_css_selector_child_match,
241 gtk_css_selector_child_tree_match,
242 gtk_css_selector_child_get_change,
243 FALSE, FALSE, FALSE, FALSE, FALSE
249 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
252 g_string_append (string, " ~ ");
256 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
257 const GtkCssMatcher *matcher)
259 GtkCssMatcher previous;
261 while (_gtk_css_matcher_get_previous (&previous, matcher))
265 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
273 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
274 const GtkCssMatcher *matcher,
277 GtkCssMatcher previous;
278 const GtkCssSelectorTree *prev;
280 while (_gtk_css_matcher_get_previous (&previous, matcher))
284 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
285 gtk_css_selector_tree_match (prev, matcher, res);
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))
295 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector)
297 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
300 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
302 gtk_css_selector_sibling_print,
303 gtk_css_selector_sibling_match,
304 gtk_css_selector_sibling_tree_match,
305 gtk_css_selector_sibling_get_change,
306 FALSE, FALSE, FALSE, FALSE, FALSE
312 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
315 g_string_append (string, " + ");
319 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
320 const GtkCssMatcher *matcher)
322 GtkCssMatcher previous;
324 if (!_gtk_css_matcher_get_previous (&previous, matcher))
327 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
331 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
332 const GtkCssMatcher *matcher,
335 GtkCssMatcher previous;
336 const GtkCssSelectorTree *prev;
338 if (!_gtk_css_matcher_get_previous (&previous, matcher))
343 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
344 gtk_css_selector_tree_match (prev, matcher, res);
348 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector)
350 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
353 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
355 gtk_css_selector_adjacent_print,
356 gtk_css_selector_adjacent_match,
357 gtk_css_selector_adjacent_tree_match,
358 gtk_css_selector_adjacent_get_change,
359 FALSE, FALSE, FALSE, FALSE, FALSE
365 gtk_css_selector_any_print (const GtkCssSelector *selector,
368 g_string_append_c (string, '*');
372 gtk_css_selector_any_match (const GtkCssSelector *selector,
373 const GtkCssMatcher *matcher)
375 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
378 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
379 _gtk_css_matcher_has_regions (matcher))
381 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
385 return gtk_css_selector_match (previous, matcher);
389 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
390 const GtkCssMatcher *matcher,
393 const GtkCssSelectorTree *prev, *prev2;
395 gtk_css_selector_tree_found_match (tree, res);
397 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
399 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
400 _gtk_css_matcher_has_regions (matcher))
402 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
403 gtk_css_selector_tree_match (prev2, matcher, res);
406 gtk_css_selector_tree_match (prev, matcher, res);
411 gtk_css_selector_any_get_change (const GtkCssSelector *selector)
413 return gtk_css_selector_get_change (gtk_css_selector_previous (selector));
416 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
418 gtk_css_selector_any_print,
419 gtk_css_selector_any_match,
420 gtk_css_selector_any_tree_match,
421 gtk_css_selector_any_get_change,
422 FALSE, FALSE, FALSE, TRUE, TRUE
428 gtk_css_selector_name_print (const GtkCssSelector *selector,
431 g_string_append (string, selector->data);
435 gtk_css_selector_name_match (const GtkCssSelector *selector,
436 const GtkCssMatcher *matcher)
438 if (!_gtk_css_matcher_has_name (matcher, selector->data))
441 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
445 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
446 const GtkCssMatcher *matcher,
449 const GtkCssSelectorTree *prev;
451 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
454 gtk_css_selector_tree_found_match (tree, res);
456 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
457 gtk_css_selector_tree_match (prev, matcher, res);
462 gtk_css_selector_name_get_change (const GtkCssSelector *selector)
464 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_NAME;
467 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
469 gtk_css_selector_name_print,
470 gtk_css_selector_name_match,
471 gtk_css_selector_name_tree_match,
472 gtk_css_selector_name_get_change,
473 FALSE, FALSE, TRUE, TRUE, FALSE
479 gtk_css_selector_region_print (const GtkCssSelector *selector,
482 g_string_append (string, selector->data);
486 gtk_css_selector_region_match (const GtkCssSelector *selector,
487 const GtkCssMatcher *matcher)
489 const GtkCssSelector *previous;
491 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
494 previous = gtk_css_selector_previous (selector);
495 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
496 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
499 return gtk_css_selector_match (previous, matcher);
503 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
504 const GtkCssMatcher *matcher,
507 const GtkCssSelectorTree *prev, *prev2;
509 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
512 gtk_css_selector_tree_found_match (tree, res);
514 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
516 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
518 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
519 gtk_css_selector_tree_match (prev2, matcher, res);
522 gtk_css_selector_tree_match (prev, matcher, res);
527 gtk_css_selector_region_get_change (const GtkCssSelector *selector)
531 change = gtk_css_selector_get_change (gtk_css_selector_previous (selector));
532 change |= GTK_CSS_CHANGE_REGION;
533 change |= _gtk_css_change_for_child (change);
538 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
540 gtk_css_selector_region_print,
541 gtk_css_selector_region_match,
542 gtk_css_selector_region_tree_match,
543 gtk_css_selector_region_get_change,
544 FALSE, FALSE, TRUE, TRUE, TRUE
550 gtk_css_selector_class_print (const GtkCssSelector *selector,
553 g_string_append_c (string, '.');
554 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
558 gtk_css_selector_class_match (const GtkCssSelector *selector,
559 const GtkCssMatcher *matcher)
561 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
564 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
568 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
569 const GtkCssMatcher *matcher,
572 const GtkCssSelectorTree *prev;
574 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
577 gtk_css_selector_tree_found_match (tree, res);
579 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
580 gtk_css_selector_tree_match (prev, matcher, res);
584 gtk_css_selector_class_get_change (const GtkCssSelector *selector)
586 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_CLASS;
589 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
591 gtk_css_selector_class_print,
592 gtk_css_selector_class_match,
593 gtk_css_selector_class_tree_match,
594 gtk_css_selector_class_get_change,
595 FALSE, TRUE, FALSE, TRUE, FALSE
601 gtk_css_selector_id_print (const GtkCssSelector *selector,
604 g_string_append_c (string, '#');
605 g_string_append (string, selector->data);
609 gtk_css_selector_id_match (const GtkCssSelector *selector,
610 const GtkCssMatcher *matcher)
612 if (!_gtk_css_matcher_has_id (matcher, selector->data))
615 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
619 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
620 const GtkCssMatcher *matcher,
623 const GtkCssSelectorTree *prev;
625 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
628 gtk_css_selector_tree_found_match (tree, res);
630 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
631 gtk_css_selector_tree_match (prev, matcher, res);
635 gtk_css_selector_id_get_change (const GtkCssSelector *selector)
637 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_ID;
640 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
642 gtk_css_selector_id_print,
643 gtk_css_selector_id_match,
644 gtk_css_selector_id_tree_match,
645 gtk_css_selector_id_get_change,
646 TRUE, FALSE, FALSE, TRUE, FALSE
649 /* PSEUDOCLASS FOR STATE */
652 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
655 static const char * state_names[] = {
666 state = GPOINTER_TO_UINT (selector->data);
667 g_string_append_c (string, ':');
669 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
671 if (state == (1 << i))
673 g_string_append (string, state_names[i]);
678 g_assert_not_reached ();
682 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
683 const GtkCssMatcher *matcher)
685 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
687 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
690 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
694 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
695 const GtkCssMatcher *matcher,
698 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
699 const GtkCssSelectorTree *prev;
701 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
704 gtk_css_selector_tree_found_match (tree, res);
706 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
707 gtk_css_selector_tree_match (prev, matcher, res);
712 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector)
714 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_STATE;
717 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
719 gtk_css_selector_pseudoclass_state_print,
720 gtk_css_selector_pseudoclass_state_match,
721 gtk_css_selector_pseudoclass_state_tree_match,
722 gtk_css_selector_pseudoclass_state_get_change,
723 FALSE, TRUE, FALSE, TRUE, FALSE
726 /* PSEUDOCLASS FOR POSITION */
734 #define POSITION_TYPE_BITS 2
735 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
738 encode_position (PositionType type,
745 gssize type :POSITION_TYPE_BITS;
746 gssize a :POSITION_NUMBER_BITS;
747 gssize b :POSITION_NUMBER_BITS;
750 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
752 g_assert (type < (1 << POSITION_TYPE_BITS));
754 result.data.type = type;
762 decode_position (const GtkCssSelector *selector,
770 gssize type :POSITION_TYPE_BITS;
771 gssize a :POSITION_NUMBER_BITS;
772 gssize b :POSITION_NUMBER_BITS;
775 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
777 result.p = selector->data;
779 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
785 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
791 decode_position (selector, &type, &a, &b);
794 case POSITION_FORWARD:
798 g_string_append (string, ":first-child");
800 g_string_append_printf (string, ":nth-child(%d)", b);
802 else if (a == 2 && b == 0)
803 g_string_append (string, ":nth-child(even)");
804 else if (a == 2 && b == 1)
805 g_string_append (string, ":nth-child(odd)");
808 g_string_append (string, ":nth-child(");
810 g_string_append (string, "n");
812 g_string_append (string, "-n");
814 g_string_append_printf (string, "%dn", a);
816 g_string_append_printf (string, "+%d)", b);
818 g_string_append_printf (string, "%d)", b);
820 g_string_append (string, ")");
823 case POSITION_BACKWARD:
827 g_string_append (string, ":last-child");
829 g_string_append_printf (string, ":nth-last-child(%d)", b);
831 else if (a == 2 && b == 0)
832 g_string_append (string, ":nth-last-child(even)");
833 else if (a == 2 && b == 1)
834 g_string_append (string, ":nth-last-child(odd)");
837 g_string_append (string, ":nth-last-child(");
839 g_string_append (string, "n");
841 g_string_append (string, "-n");
843 g_string_append_printf (string, "%dn", a);
845 g_string_append_printf (string, "+%d)", b);
847 g_string_append_printf (string, "%d)", b);
849 g_string_append (string, ")");
853 g_string_append (string, ":only-child");
855 case POSITION_SORTED:
856 g_string_append (string, ":sorted");
859 g_assert_not_reached ();
865 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
866 const GtkCssMatcher *matcher)
868 GtkRegionFlags selector_flags;
869 const GtkCssSelector *previous;
873 decode_position (selector, &type, &a, &b);
876 case POSITION_FORWARD:
877 if (a == 0 && b == 1)
878 selector_flags = GTK_REGION_FIRST;
879 else if (a == 2 && b == 0)
880 selector_flags = GTK_REGION_EVEN;
881 else if (a == 2 && b == 1)
882 selector_flags = GTK_REGION_ODD;
886 case POSITION_BACKWARD:
887 if (a == 0 && b == 1)
888 selector_flags = GTK_REGION_LAST;
893 selector_flags = GTK_REGION_ONLY;
895 case POSITION_SORTED:
896 selector_flags = GTK_REGION_SORTED;
899 g_assert_not_reached ();
902 selector = gtk_css_selector_previous (selector);
904 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
907 previous = gtk_css_selector_previous (selector);
908 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
909 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
912 return gtk_css_selector_match (previous, matcher);
916 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
917 const GtkCssMatcher *matcher)
919 const GtkCssSelector *previous;
923 previous = gtk_css_selector_previous (selector);
924 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
925 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
927 decode_position (selector, &type, &a, &b);
930 case POSITION_FORWARD:
931 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
934 case POSITION_BACKWARD:
935 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
939 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
940 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
943 case POSITION_SORTED:
946 g_assert_not_reached ();
950 return gtk_css_selector_match (previous, matcher);
954 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
955 const GtkCssSelectorTree *prev,
956 const GtkCssMatcher *matcher,
959 const GtkCssSelectorTree *prev2;
960 GtkRegionFlags selector_flags;
964 decode_position (&tree->selector, &type, &a, &b);
967 case POSITION_FORWARD:
968 if (a == 0 && b == 1)
969 selector_flags = GTK_REGION_FIRST;
970 else if (a == 2 && b == 0)
971 selector_flags = GTK_REGION_EVEN;
972 else if (a == 2 && b == 1)
973 selector_flags = GTK_REGION_ODD;
977 case POSITION_BACKWARD:
978 if (a == 0 && b == 1)
979 selector_flags = GTK_REGION_LAST;
984 selector_flags = GTK_REGION_ONLY;
986 case POSITION_SORTED:
987 selector_flags = GTK_REGION_SORTED;
990 g_assert_not_reached ();
994 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
997 gtk_css_selector_tree_found_match (prev, res);
999 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
1001 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1002 gtk_css_selector_tree_match (prev2->previous, matcher, res);
1003 gtk_css_selector_tree_match (prev2, matcher, res);
1008 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1009 const GtkCssMatcher *matcher,
1012 const GtkCssSelectorTree *prev;
1016 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
1018 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1019 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1022 decode_position (&tree->selector, &type, &a, &b);
1025 case POSITION_FORWARD:
1026 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1029 case POSITION_BACKWARD:
1030 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1034 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1035 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1038 case POSITION_SORTED:
1041 g_assert_not_reached ();
1045 gtk_css_selector_tree_found_match (tree, res);
1047 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
1049 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1050 gtk_css_selector_tree_match (prev, matcher, res);
1055 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector)
1057 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_POSITION;
1060 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1061 "pseudoclass-position",
1062 gtk_css_selector_pseudoclass_position_print,
1063 gtk_css_selector_pseudoclass_position_match,
1064 gtk_css_selector_pseudoclass_position_tree_match,
1065 gtk_css_selector_pseudoclass_position_get_change,
1066 FALSE, TRUE, FALSE, TRUE, TRUE
1072 gtk_css_selector_size (const GtkCssSelector *selector)
1078 selector = gtk_css_selector_previous (selector);
1085 static GtkCssSelector *
1086 gtk_css_selector_new (const GtkCssSelectorClass *class,
1087 GtkCssSelector *selector,
1092 size = gtk_css_selector_size (selector);
1093 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1095 selector[1].class = NULL;
1097 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1099 selector->class = class;
1100 selector->data = data;
1105 static GtkCssSelector *
1106 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1110 name = _gtk_css_parser_try_name (parser, FALSE);
1114 _gtk_css_parser_error (parser, "Expected a valid name for class");
1116 _gtk_css_selector_free (selector);
1120 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1122 GUINT_TO_POINTER (g_quark_from_string (name)));
1129 static GtkCssSelector *
1130 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1134 name = _gtk_css_parser_try_name (parser, FALSE);
1138 _gtk_css_parser_error (parser, "Expected a valid name for id");
1140 _gtk_css_selector_free (selector);
1144 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1146 g_intern_string (name));
1153 static GtkCssSelector *
1154 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1155 GtkCssSelector *selector,
1160 if (!_gtk_css_parser_try (parser, "(", TRUE))
1162 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1164 _gtk_css_selector_free (selector);
1168 if (_gtk_css_parser_try (parser, "even", TRUE))
1173 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1178 else if (type == POSITION_FORWARD &&
1179 _gtk_css_parser_try (parser, "first", TRUE))
1184 else if (type == POSITION_FORWARD &&
1185 _gtk_css_parser_try (parser, "last", TRUE))
1189 type = POSITION_BACKWARD;
1195 if (_gtk_css_parser_try (parser, "+", TRUE))
1197 else if (_gtk_css_parser_try (parser, "-", TRUE))
1202 if (_gtk_css_parser_try_int (parser, &a))
1206 _gtk_css_parser_error (parser, "Expected an integer");
1208 _gtk_css_selector_free (selector);
1213 else if (_gtk_css_parser_has_prefix (parser, "n"))
1219 _gtk_css_parser_error (parser, "Expected an integer");
1221 _gtk_css_selector_free (selector);
1225 if (_gtk_css_parser_try (parser, "n", TRUE))
1227 if (_gtk_css_parser_try (parser, "+", TRUE))
1229 else if (_gtk_css_parser_try (parser, "-", TRUE))
1234 if (_gtk_css_parser_try_int (parser, &b))
1238 _gtk_css_parser_error (parser, "Expected an integer");
1240 _gtk_css_selector_free (selector);
1256 if (!_gtk_css_parser_try (parser, ")", FALSE))
1258 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1260 _gtk_css_selector_free (selector);
1264 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1266 encode_position (type, a, b));
1271 static GtkCssSelector *
1272 parse_selector_pseudo_class (GtkCssParser *parser,
1273 GtkCssSelector *selector)
1275 static const struct {
1277 GtkStateFlags state_flag;
1278 PositionType position_type;
1281 } pseudo_classes[] = {
1282 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1283 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1284 { "only-child", 0, POSITION_ONLY, 0, 0 },
1285 { "sorted", 0, POSITION_SORTED, 0, 0 },
1286 { "active", GTK_STATE_FLAG_ACTIVE, },
1287 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1288 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1289 { "selected", GTK_STATE_FLAG_SELECTED, },
1290 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1291 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1292 { "focused", GTK_STATE_FLAG_FOCUSED, },
1293 { "focus", GTK_STATE_FLAG_FOCUSED, },
1294 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
1298 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1299 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1300 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1301 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1303 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1305 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1307 if (pseudo_classes[i].state_flag)
1308 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1310 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1312 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1314 encode_position (pseudo_classes[i].position_type,
1315 pseudo_classes[i].position_a,
1316 pseudo_classes[i].position_b));
1321 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1323 _gtk_css_selector_free (selector);
1327 static GtkCssSelector *
1328 try_parse_name (GtkCssParser *parser,
1329 GtkCssSelector *selector)
1333 name = _gtk_css_parser_try_ident (parser, FALSE);
1336 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
1337 ? >K_CSS_SELECTOR_REGION
1338 : >K_CSS_SELECTOR_NAME,
1340 g_intern_string (name));
1343 else if (_gtk_css_parser_try (parser, "*", FALSE))
1344 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1349 static GtkCssSelector *
1350 parse_simple_selector (GtkCssParser *parser,
1351 GtkCssSelector *selector)
1353 guint size = gtk_css_selector_size (selector);
1355 selector = try_parse_name (parser, selector);
1358 if (_gtk_css_parser_try (parser, "#", FALSE))
1359 selector = parse_selector_id (parser, selector);
1360 else if (_gtk_css_parser_try (parser, ".", FALSE))
1361 selector = parse_selector_class (parser, selector);
1362 else if (_gtk_css_parser_try (parser, ":", FALSE))
1363 selector = parse_selector_pseudo_class (parser, selector);
1364 else if (gtk_css_selector_size (selector) == size)
1366 _gtk_css_parser_error (parser, "Expected a valid selector");
1368 _gtk_css_selector_free (selector);
1374 while (selector && !_gtk_css_parser_is_eof (parser));
1376 _gtk_css_parser_skip_whitespace (parser);
1382 _gtk_css_selector_parse (GtkCssParser *parser)
1384 GtkCssSelector *selector = NULL;
1386 while ((selector = parse_simple_selector (parser, selector)) &&
1387 !_gtk_css_parser_is_eof (parser) &&
1388 !_gtk_css_parser_begins_with (parser, ',') &&
1389 !_gtk_css_parser_begins_with (parser, '{'))
1391 if (_gtk_css_parser_try (parser, "+", TRUE))
1392 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1393 else if (_gtk_css_parser_try (parser, "~", TRUE))
1394 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1395 else if (_gtk_css_parser_try (parser, ">", TRUE))
1396 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1398 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1405 _gtk_css_selector_free (GtkCssSelector *selector)
1407 g_return_if_fail (selector != NULL);
1413 _gtk_css_selector_print (const GtkCssSelector *selector,
1416 const GtkCssSelector *previous;
1418 g_return_if_fail (selector != NULL);
1420 previous = gtk_css_selector_previous (selector);
1422 _gtk_css_selector_print (previous, str);
1424 selector->class->print (selector, str);
1428 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1432 g_return_val_if_fail (selector != NULL, NULL);
1434 string = g_string_new (NULL);
1436 _gtk_css_selector_print (selector, string);
1438 return g_string_free (string, FALSE);
1442 _gtk_css_selector_get_change (const GtkCssSelector *selector)
1444 g_return_val_if_fail (selector != NULL, 0);
1446 return gtk_css_selector_get_change (selector);
1450 * _gtk_css_selector_matches:
1451 * @selector: the selector
1452 * @path: the path to check
1453 * @state: The state to match
1455 * Checks if the @selector matches the given @path. If @length is
1456 * smaller than the number of elements in @path, it is assumed that
1457 * only the first @length element of @path are valid and the rest
1458 * does not exist. This is useful for doing parent matches for the
1459 * 'inherit' keyword.
1461 * Returns: %TRUE if the selector matches @path
1464 _gtk_css_selector_matches (const GtkCssSelector *selector,
1465 const GtkCssMatcher *matcher)
1468 g_return_val_if_fail (selector != NULL, FALSE);
1469 g_return_val_if_fail (matcher != NULL, FALSE);
1471 return gtk_css_selector_match (selector, matcher);
1474 /* Computes specificity according to CSS 2.1.
1475 * The arguments must be initialized to 0 */
1477 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1482 for (; selector; selector = gtk_css_selector_previous (selector))
1484 const GtkCssSelectorClass *klass = selector->class;
1486 if (klass->increase_id_specificity)
1488 if (klass->increase_class_specificity)
1490 if (klass->increase_element_specificity)
1496 _gtk_css_selector_compare (const GtkCssSelector *a,
1497 const GtkCssSelector *b)
1499 guint a_ids = 0, a_classes = 0, a_elements = 0;
1500 guint b_ids = 0, b_classes = 0, b_elements = 0;
1503 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1504 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1506 compare = a_ids - b_ids;
1510 compare = a_classes - b_classes;
1514 return a_elements - b_elements;
1518 /******************** SelectorTree handling *****************/
1521 gtk_css_selectors_count_initial_init (void)
1523 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
1527 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
1529 if (!selector->class->is_simple || selector->class->must_keep_order)
1531 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1532 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1537 selector && selector->class->is_simple && !selector->class->must_keep_order;
1538 selector = gtk_css_selector_previous (selector))
1540 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1541 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1546 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
1548 if (!selector->class->is_simple || selector->class->must_keep_order)
1549 return gtk_css_selector_equal (selector, initial);
1552 selector && selector->class->is_simple && !selector->class->must_keep_order;
1553 selector = gtk_css_selector_previous (selector))
1555 if (gtk_css_selector_equal (selector, initial))
1562 static GtkCssSelector *
1563 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
1565 GtkCssSelector *found;
1568 /* If the initial simple selector is not first, move it there so we can skip it
1569 without losing any other selectors */
1570 if (!gtk_css_selector_equal (selector, initial))
1572 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
1574 if (gtk_css_selector_equal (found, initial))
1578 g_assert (found != NULL && found->class->is_simple);
1585 return (GtkCssSelector *)gtk_css_selector_previous (selector);
1589 direct_ptr_compare (const void *_a, const void *_b)
1591 gpointer *a = (gpointer *)_a;
1592 gpointer *b = (gpointer *)_b;
1601 _gtk_css_selector_tree_match_all (GtkCssSelectorTree *tree,
1602 const GtkCssMatcher *matcher)
1606 GHashTableIter iter;
1609 res = g_hash_table_new (g_direct_hash, g_direct_equal);
1611 for (; tree != NULL; tree = tree->siblings)
1612 gtk_css_selector_tree_match (tree, matcher, res);
1614 array = g_ptr_array_sized_new (g_hash_table_size (res));
1616 g_hash_table_iter_init (&iter, res);
1617 while (g_hash_table_iter_next (&iter, &key, NULL))
1618 g_ptr_array_add (array, key);
1620 g_hash_table_destroy (res);
1622 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
1629 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
1631 gboolean first = TRUE;
1634 for (; tree != NULL; tree = tree->siblings, first = FALSE)
1637 g_string_append (str, prefix);
1642 g_string_append (str, "─┬─");
1644 g_string_append (str, "───");
1649 g_string_append (str, " ├─");
1651 g_string_append (str, " └─");
1655 tree->selector.class->print (&tree->selector, str);
1656 len = str->len - len;
1660 GString *prefix2 = g_string_new (prefix);
1663 g_string_append (prefix2, " │ ");
1665 g_string_append (prefix2, " ");
1666 for (i = 0; i < len; i++)
1667 g_string_append_c (prefix2, ' ');
1669 _gtk_css_selector_tree_print (tree->previous, str, prefix2->str);
1670 g_string_free (prefix2, TRUE);
1673 g_string_append (str, "\n");
1679 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
1684 _gtk_css_selector_tree_free (tree->siblings);
1685 _gtk_css_selector_tree_free (tree->previous);
1693 GtkCssSelector *current_selector;
1694 } GtkCssSelectorRuleSetInfo;
1697 static GtkCssSelectorTree *
1698 subdivide_infos (GList *infos, GtkCssSelectorTree *parent)
1700 GHashTable *ht = gtk_css_selectors_count_initial_init ();
1704 GtkCssSelectorTree *tree;
1705 GtkCssSelectorRuleSetInfo *info;
1706 GtkCssSelector *max_selector;
1707 GHashTableIter iter;
1709 gpointer key, value;
1710 GPtrArray *exact_matches;
1715 for (l = infos; l != NULL; l = l->next)
1718 gtk_css_selectors_count_initial (info->current_selector, ht);
1721 /* Pick the selector with highest count, and use as decision on this level
1722 as that makes it possible to skip the largest amount of checks later */
1725 max_selector = NULL;
1727 g_hash_table_iter_init (&iter, ht);
1728 while (g_hash_table_iter_next (&iter, &key, &value))
1730 if (GPOINTER_TO_UINT (value) > max_count)
1732 max_count = GPOINTER_TO_UINT (value);
1740 tree = g_new0 (GtkCssSelectorTree, 1);
1741 tree->parent = parent;
1742 tree->selector = *max_selector;
1744 exact_matches = NULL;
1745 for (l = infos; l != NULL; l = l->next)
1749 if (gtk_css_selectors_has_initial_selector (info->current_selector, &tree->selector))
1751 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &tree->selector);
1752 if (info->current_selector == NULL)
1754 /* Matches current node */
1755 if (exact_matches == NULL)
1756 exact_matches = g_ptr_array_new ();
1757 g_ptr_array_add (exact_matches, info->match);
1760 matched = g_list_prepend (matched, info);
1764 remaining = g_list_prepend (remaining, info);
1770 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
1771 tree->matches = g_ptr_array_free (exact_matches, FALSE);
1775 tree->previous = subdivide_infos (matched, tree);
1778 tree->siblings = subdivide_infos (remaining, parent);
1783 struct _GtkCssSelectorTreeBuilder {
1787 GtkCssSelectorTreeBuilder *
1788 _gtk_css_selector_tree_builder_new (void)
1790 return g_new0 (GtkCssSelectorTreeBuilder, 1);
1794 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
1796 g_list_free_full (builder->infos, g_free);
1801 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
1802 GtkCssSelector *selectors,
1805 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
1807 info->match = match;
1808 info->current_selector = selectors;
1809 builder->infos = g_list_prepend (builder->infos, info);
1812 GtkCssSelectorTree *
1813 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
1815 GtkCssSelectorTree *tree;
1817 tree = subdivide_infos (builder->infos, NULL);
1821 GString *s = g_string_new ("");
1822 _gtk_css_selector_tree_print (tree, s, "");
1823 g_print ("%s", s->str);
1824 g_string_free (s, TRUE);