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);
41 int (* compare_one) (const GtkCssSelector *a,
42 const GtkCssSelector *b);
44 guint increase_id_specificity :1;
45 guint increase_class_specificity :1;
46 guint increase_element_specificity :1;
48 guint must_keep_order :1; /* Due to region weirdness these must be kept before a DESCENDANT, so don't reorder */
51 struct _GtkCssSelector
53 const GtkCssSelectorClass *class; /* type of check this selector does */
54 gconstpointer data; /* data for matching:
55 - interned string for CLASS, NAME and ID
56 - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
59 struct _GtkCssSelectorTree
61 GtkCssSelector selector;
62 GtkCssSelectorTree *parent;
63 GtkCssSelectorTree *previous;
64 GtkCssSelectorTree *siblings;
65 gpointer *matches; /* pointers that we return as matches if selector matches */
69 gtk_css_selector_equal (const GtkCssSelector *a,
70 const GtkCssSelector *b)
73 a->class == b->class &&
78 gtk_css_selector_hash (const GtkCssSelector *selector)
80 return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
84 gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
91 for (i = 0; tree->matches[i] != NULL; i++)
92 g_hash_table_insert (res, tree->matches[i], tree->matches[i]);
97 gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
98 const GtkCssMatcher *matcher,
104 tree->selector.class->tree_match (tree, matcher, res);
108 gtk_css_selector_match (const GtkCssSelector *selector,
109 const GtkCssMatcher *matcher)
111 if (selector == NULL)
114 return selector->class->match (selector, matcher);
118 gtk_css_selector_get_change (const GtkCssSelector *selector)
120 if (selector == NULL)
123 return selector->class->get_change (selector);
127 gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
129 if (a->class != b->class)
130 return strcmp (a->class->name, b->class->name);
132 return a->class->compare_one (a, b);
135 static const GtkCssSelector *
136 gtk_css_selector_previous (const GtkCssSelector *selector)
138 selector = selector + 1;
140 return selector->class ? selector : NULL;
146 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
149 g_string_append_c (string, ' ');
153 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
154 const GtkCssMatcher *matcher)
156 GtkCssMatcher ancestor;
158 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
162 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
170 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
171 const GtkCssMatcher *matcher,
174 GtkCssMatcher ancestor;
175 const GtkCssSelectorTree *prev;
177 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
181 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
182 gtk_css_selector_tree_match (prev, matcher, res);
184 /* any matchers are dangerous here, as we may loop forever, but
185 we can terminate now as all possible matches have already been added */
186 if (_gtk_css_matcher_matches_any (matcher))
192 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
193 const GtkCssSelector *b)
199 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector)
201 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
204 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
206 gtk_css_selector_descendant_print,
207 gtk_css_selector_descendant_match,
208 gtk_css_selector_descendant_tree_match,
209 gtk_css_selector_descendant_get_change,
210 gtk_css_selector_descendant_compare_one,
211 FALSE, FALSE, FALSE, FALSE, FALSE
217 gtk_css_selector_child_print (const GtkCssSelector *selector,
220 g_string_append (string, " > ");
224 gtk_css_selector_child_match (const GtkCssSelector *selector,
225 const GtkCssMatcher *matcher)
227 GtkCssMatcher parent;
229 if (!_gtk_css_matcher_get_parent (&parent, matcher))
232 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
236 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
237 const GtkCssMatcher *matcher,
240 GtkCssMatcher parent;
241 const GtkCssSelectorTree *prev;
243 if (!_gtk_css_matcher_get_parent (&parent, matcher))
246 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
247 gtk_css_selector_tree_match (prev, &parent, res);
251 gtk_css_selector_child_get_change (const GtkCssSelector *selector)
253 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
257 gtk_css_selector_child_compare_one (const GtkCssSelector *a,
258 const GtkCssSelector *b)
263 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
265 gtk_css_selector_child_print,
266 gtk_css_selector_child_match,
267 gtk_css_selector_child_tree_match,
268 gtk_css_selector_child_get_change,
269 gtk_css_selector_child_compare_one,
270 FALSE, FALSE, FALSE, FALSE, FALSE
276 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
279 g_string_append (string, " ~ ");
283 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
284 const GtkCssMatcher *matcher)
286 GtkCssMatcher previous;
288 while (_gtk_css_matcher_get_previous (&previous, matcher))
292 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
300 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
301 const GtkCssMatcher *matcher,
304 GtkCssMatcher previous;
305 const GtkCssSelectorTree *prev;
307 while (_gtk_css_matcher_get_previous (&previous, matcher))
311 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
312 gtk_css_selector_tree_match (prev, matcher, res);
314 /* any matchers are dangerous here, as we may loop forever, but
315 we can terminate now as all possible matches have already been added */
316 if (_gtk_css_matcher_matches_any (matcher))
322 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector)
324 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
328 gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
329 const GtkCssSelector *b)
335 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
337 gtk_css_selector_sibling_print,
338 gtk_css_selector_sibling_match,
339 gtk_css_selector_sibling_tree_match,
340 gtk_css_selector_sibling_get_change,
341 gtk_css_selector_sibling_compare_one,
342 FALSE, FALSE, FALSE, FALSE, FALSE
348 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
351 g_string_append (string, " + ");
355 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
356 const GtkCssMatcher *matcher)
358 GtkCssMatcher previous;
360 if (!_gtk_css_matcher_get_previous (&previous, matcher))
363 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
367 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
368 const GtkCssMatcher *matcher,
371 GtkCssMatcher previous;
372 const GtkCssSelectorTree *prev;
374 if (!_gtk_css_matcher_get_previous (&previous, matcher))
379 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
380 gtk_css_selector_tree_match (prev, matcher, res);
384 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector)
386 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
390 gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
391 const GtkCssSelector *b)
396 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
398 gtk_css_selector_adjacent_print,
399 gtk_css_selector_adjacent_match,
400 gtk_css_selector_adjacent_tree_match,
401 gtk_css_selector_adjacent_get_change,
402 gtk_css_selector_adjacent_compare_one,
403 FALSE, FALSE, FALSE, FALSE, FALSE
409 gtk_css_selector_any_print (const GtkCssSelector *selector,
412 g_string_append_c (string, '*');
416 gtk_css_selector_any_match (const GtkCssSelector *selector,
417 const GtkCssMatcher *matcher)
419 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
422 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
423 _gtk_css_matcher_has_regions (matcher))
425 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
429 return gtk_css_selector_match (previous, matcher);
433 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
434 const GtkCssMatcher *matcher,
437 const GtkCssSelectorTree *prev, *prev2;
439 gtk_css_selector_tree_found_match (tree, res);
441 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
443 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
444 _gtk_css_matcher_has_regions (matcher))
446 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
447 gtk_css_selector_tree_match (prev2, matcher, res);
450 gtk_css_selector_tree_match (prev, matcher, res);
455 gtk_css_selector_any_get_change (const GtkCssSelector *selector)
457 return gtk_css_selector_get_change (gtk_css_selector_previous (selector));
461 gtk_css_selector_any_compare_one (const GtkCssSelector *a,
462 const GtkCssSelector *b)
467 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
469 gtk_css_selector_any_print,
470 gtk_css_selector_any_match,
471 gtk_css_selector_any_tree_match,
472 gtk_css_selector_any_get_change,
473 gtk_css_selector_any_compare_one,
474 FALSE, FALSE, FALSE, TRUE, TRUE
480 gtk_css_selector_name_print (const GtkCssSelector *selector,
483 g_string_append (string, selector->data);
487 gtk_css_selector_name_match (const GtkCssSelector *selector,
488 const GtkCssMatcher *matcher)
490 if (!_gtk_css_matcher_has_name (matcher, selector->data))
493 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
497 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
498 const GtkCssMatcher *matcher,
501 const GtkCssSelectorTree *prev;
503 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
506 gtk_css_selector_tree_found_match (tree, res);
508 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
509 gtk_css_selector_tree_match (prev, matcher, res);
514 gtk_css_selector_name_get_change (const GtkCssSelector *selector)
516 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_NAME;
520 gtk_css_selector_name_compare_one (const GtkCssSelector *a,
521 const GtkCssSelector *b)
523 return strcmp (a->data, b->data);
526 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
528 gtk_css_selector_name_print,
529 gtk_css_selector_name_match,
530 gtk_css_selector_name_tree_match,
531 gtk_css_selector_name_get_change,
532 gtk_css_selector_name_compare_one,
533 FALSE, FALSE, TRUE, TRUE, FALSE
539 gtk_css_selector_region_print (const GtkCssSelector *selector,
542 g_string_append (string, selector->data);
546 gtk_css_selector_region_match (const GtkCssSelector *selector,
547 const GtkCssMatcher *matcher)
549 const GtkCssSelector *previous;
551 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
554 previous = gtk_css_selector_previous (selector);
555 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
556 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
559 return gtk_css_selector_match (previous, matcher);
563 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
564 const GtkCssMatcher *matcher,
567 const GtkCssSelectorTree *prev, *prev2;
569 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
572 gtk_css_selector_tree_found_match (tree, res);
574 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
576 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
578 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
579 gtk_css_selector_tree_match (prev2, matcher, res);
582 gtk_css_selector_tree_match (prev, matcher, res);
587 gtk_css_selector_region_get_change (const GtkCssSelector *selector)
591 change = gtk_css_selector_get_change (gtk_css_selector_previous (selector));
592 change |= GTK_CSS_CHANGE_REGION;
593 change |= _gtk_css_change_for_child (change);
599 gtk_css_selector_region_compare_one (const GtkCssSelector *a,
600 const GtkCssSelector *b)
602 return strcmp (a->data, b->data);
605 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
607 gtk_css_selector_region_print,
608 gtk_css_selector_region_match,
609 gtk_css_selector_region_tree_match,
610 gtk_css_selector_region_get_change,
611 gtk_css_selector_region_compare_one,
612 FALSE, FALSE, TRUE, TRUE, TRUE
618 gtk_css_selector_class_print (const GtkCssSelector *selector,
621 g_string_append_c (string, '.');
622 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
626 gtk_css_selector_class_match (const GtkCssSelector *selector,
627 const GtkCssMatcher *matcher)
629 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
632 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
636 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
637 const GtkCssMatcher *matcher,
640 const GtkCssSelectorTree *prev;
642 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
645 gtk_css_selector_tree_found_match (tree, res);
647 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
648 gtk_css_selector_tree_match (prev, matcher, res);
652 gtk_css_selector_class_get_change (const GtkCssSelector *selector)
654 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_CLASS;
658 gtk_css_selector_class_compare_one (const GtkCssSelector *a,
659 const GtkCssSelector *b)
661 return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
662 g_quark_to_string (GPOINTER_TO_UINT (b->data)));
665 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
667 gtk_css_selector_class_print,
668 gtk_css_selector_class_match,
669 gtk_css_selector_class_tree_match,
670 gtk_css_selector_class_get_change,
671 gtk_css_selector_class_compare_one,
672 FALSE, TRUE, FALSE, TRUE, FALSE
678 gtk_css_selector_id_print (const GtkCssSelector *selector,
681 g_string_append_c (string, '#');
682 g_string_append (string, selector->data);
686 gtk_css_selector_id_match (const GtkCssSelector *selector,
687 const GtkCssMatcher *matcher)
689 if (!_gtk_css_matcher_has_id (matcher, selector->data))
692 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
696 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
697 const GtkCssMatcher *matcher,
700 const GtkCssSelectorTree *prev;
702 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
705 gtk_css_selector_tree_found_match (tree, res);
707 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
708 gtk_css_selector_tree_match (prev, matcher, res);
712 gtk_css_selector_id_get_change (const GtkCssSelector *selector)
714 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_ID;
719 gtk_css_selector_id_compare_one (const GtkCssSelector *a,
720 const GtkCssSelector *b)
722 return strcmp (a->data, b->data);
725 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
727 gtk_css_selector_id_print,
728 gtk_css_selector_id_match,
729 gtk_css_selector_id_tree_match,
730 gtk_css_selector_id_get_change,
731 gtk_css_selector_id_compare_one,
732 TRUE, FALSE, FALSE, TRUE, FALSE
735 /* PSEUDOCLASS FOR STATE */
738 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
741 static const char * state_names[] = {
752 state = GPOINTER_TO_UINT (selector->data);
753 g_string_append_c (string, ':');
755 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
757 if (state == (1 << i))
759 g_string_append (string, state_names[i]);
764 g_assert_not_reached ();
768 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
769 const GtkCssMatcher *matcher)
771 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
773 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
776 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
780 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
781 const GtkCssMatcher *matcher,
784 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
785 const GtkCssSelectorTree *prev;
787 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
790 gtk_css_selector_tree_found_match (tree, res);
792 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
793 gtk_css_selector_tree_match (prev, matcher, res);
798 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector)
800 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_STATE;
804 gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
805 const GtkCssSelector *b)
807 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
810 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
812 gtk_css_selector_pseudoclass_state_print,
813 gtk_css_selector_pseudoclass_state_match,
814 gtk_css_selector_pseudoclass_state_tree_match,
815 gtk_css_selector_pseudoclass_state_get_change,
816 gtk_css_selector_pseudoclass_state_compare_one,
817 FALSE, TRUE, FALSE, TRUE, FALSE
820 /* PSEUDOCLASS FOR POSITION */
828 #define POSITION_TYPE_BITS 2
829 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
832 encode_position (PositionType type,
839 gssize type :POSITION_TYPE_BITS;
840 gssize a :POSITION_NUMBER_BITS;
841 gssize b :POSITION_NUMBER_BITS;
844 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
846 g_assert (type < (1 << POSITION_TYPE_BITS));
848 result.data.type = type;
856 decode_position (const GtkCssSelector *selector,
864 gssize type :POSITION_TYPE_BITS;
865 gssize a :POSITION_NUMBER_BITS;
866 gssize b :POSITION_NUMBER_BITS;
869 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
871 result.p = selector->data;
873 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
879 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
885 decode_position (selector, &type, &a, &b);
888 case POSITION_FORWARD:
892 g_string_append (string, ":first-child");
894 g_string_append_printf (string, ":nth-child(%d)", b);
896 else if (a == 2 && b == 0)
897 g_string_append (string, ":nth-child(even)");
898 else if (a == 2 && b == 1)
899 g_string_append (string, ":nth-child(odd)");
902 g_string_append (string, ":nth-child(");
904 g_string_append (string, "n");
906 g_string_append (string, "-n");
908 g_string_append_printf (string, "%dn", a);
910 g_string_append_printf (string, "+%d)", b);
912 g_string_append_printf (string, "%d)", b);
914 g_string_append (string, ")");
917 case POSITION_BACKWARD:
921 g_string_append (string, ":last-child");
923 g_string_append_printf (string, ":nth-last-child(%d)", b);
925 else if (a == 2 && b == 0)
926 g_string_append (string, ":nth-last-child(even)");
927 else if (a == 2 && b == 1)
928 g_string_append (string, ":nth-last-child(odd)");
931 g_string_append (string, ":nth-last-child(");
933 g_string_append (string, "n");
935 g_string_append (string, "-n");
937 g_string_append_printf (string, "%dn", a);
939 g_string_append_printf (string, "+%d)", b);
941 g_string_append_printf (string, "%d)", b);
943 g_string_append (string, ")");
947 g_string_append (string, ":only-child");
949 case POSITION_SORTED:
950 g_string_append (string, ":sorted");
953 g_assert_not_reached ();
959 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
960 const GtkCssMatcher *matcher)
962 GtkRegionFlags selector_flags;
963 const GtkCssSelector *previous;
967 decode_position (selector, &type, &a, &b);
970 case POSITION_FORWARD:
971 if (a == 0 && b == 1)
972 selector_flags = GTK_REGION_FIRST;
973 else if (a == 2 && b == 0)
974 selector_flags = GTK_REGION_EVEN;
975 else if (a == 2 && b == 1)
976 selector_flags = GTK_REGION_ODD;
980 case POSITION_BACKWARD:
981 if (a == 0 && b == 1)
982 selector_flags = GTK_REGION_LAST;
987 selector_flags = GTK_REGION_ONLY;
989 case POSITION_SORTED:
990 selector_flags = GTK_REGION_SORTED;
993 g_assert_not_reached ();
996 selector = gtk_css_selector_previous (selector);
998 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
1001 previous = gtk_css_selector_previous (selector);
1002 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
1003 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
1006 return gtk_css_selector_match (previous, matcher);
1010 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
1011 const GtkCssMatcher *matcher)
1013 const GtkCssSelector *previous;
1017 previous = gtk_css_selector_previous (selector);
1018 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
1019 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
1021 decode_position (selector, &type, &a, &b);
1024 case POSITION_FORWARD:
1025 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1028 case POSITION_BACKWARD:
1029 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1033 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1034 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1037 case POSITION_SORTED:
1040 g_assert_not_reached ();
1044 return gtk_css_selector_match (previous, matcher);
1048 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
1049 const GtkCssSelectorTree *prev,
1050 const GtkCssMatcher *matcher,
1053 const GtkCssSelectorTree *prev2;
1054 GtkRegionFlags selector_flags;
1058 decode_position (&tree->selector, &type, &a, &b);
1061 case POSITION_FORWARD:
1062 if (a == 0 && b == 1)
1063 selector_flags = GTK_REGION_FIRST;
1064 else if (a == 2 && b == 0)
1065 selector_flags = GTK_REGION_EVEN;
1066 else if (a == 2 && b == 1)
1067 selector_flags = GTK_REGION_ODD;
1071 case POSITION_BACKWARD:
1072 if (a == 0 && b == 1)
1073 selector_flags = GTK_REGION_LAST;
1078 selector_flags = GTK_REGION_ONLY;
1080 case POSITION_SORTED:
1081 selector_flags = GTK_REGION_SORTED;
1084 g_assert_not_reached ();
1088 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1091 gtk_css_selector_tree_found_match (prev, res);
1093 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
1095 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1096 gtk_css_selector_tree_match (prev2->previous, matcher, res);
1097 gtk_css_selector_tree_match (prev2, matcher, res);
1102 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1103 const GtkCssMatcher *matcher,
1106 const GtkCssSelectorTree *prev;
1110 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
1112 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1113 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1116 decode_position (&tree->selector, &type, &a, &b);
1119 case POSITION_FORWARD:
1120 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1123 case POSITION_BACKWARD:
1124 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1128 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1129 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1132 case POSITION_SORTED:
1135 g_assert_not_reached ();
1139 gtk_css_selector_tree_found_match (tree, res);
1141 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
1143 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1144 gtk_css_selector_tree_match (prev, matcher, res);
1149 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector)
1151 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_POSITION;
1155 gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
1156 const GtkCssSelector *b)
1158 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1161 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1162 "pseudoclass-position",
1163 gtk_css_selector_pseudoclass_position_print,
1164 gtk_css_selector_pseudoclass_position_match,
1165 gtk_css_selector_pseudoclass_position_tree_match,
1166 gtk_css_selector_pseudoclass_position_get_change,
1167 gtk_css_selector_pseudoclass_position_compare_one,
1168 FALSE, TRUE, FALSE, TRUE, TRUE
1174 gtk_css_selector_size (const GtkCssSelector *selector)
1180 selector = gtk_css_selector_previous (selector);
1187 static GtkCssSelector *
1188 gtk_css_selector_new (const GtkCssSelectorClass *class,
1189 GtkCssSelector *selector,
1194 size = gtk_css_selector_size (selector);
1195 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1197 selector[1].class = NULL;
1199 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1201 selector->class = class;
1202 selector->data = data;
1207 static GtkCssSelector *
1208 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1212 name = _gtk_css_parser_try_name (parser, FALSE);
1216 _gtk_css_parser_error (parser, "Expected a valid name for class");
1218 _gtk_css_selector_free (selector);
1222 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1224 GUINT_TO_POINTER (g_quark_from_string (name)));
1231 static GtkCssSelector *
1232 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1236 name = _gtk_css_parser_try_name (parser, FALSE);
1240 _gtk_css_parser_error (parser, "Expected a valid name for id");
1242 _gtk_css_selector_free (selector);
1246 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1248 g_intern_string (name));
1255 static GtkCssSelector *
1256 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1257 GtkCssSelector *selector,
1262 if (!_gtk_css_parser_try (parser, "(", TRUE))
1264 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1266 _gtk_css_selector_free (selector);
1270 if (_gtk_css_parser_try (parser, "even", TRUE))
1275 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1280 else if (type == POSITION_FORWARD &&
1281 _gtk_css_parser_try (parser, "first", TRUE))
1286 else if (type == POSITION_FORWARD &&
1287 _gtk_css_parser_try (parser, "last", TRUE))
1291 type = POSITION_BACKWARD;
1297 if (_gtk_css_parser_try (parser, "+", TRUE))
1299 else if (_gtk_css_parser_try (parser, "-", TRUE))
1304 if (_gtk_css_parser_try_int (parser, &a))
1308 _gtk_css_parser_error (parser, "Expected an integer");
1310 _gtk_css_selector_free (selector);
1315 else if (_gtk_css_parser_has_prefix (parser, "n"))
1321 _gtk_css_parser_error (parser, "Expected an integer");
1323 _gtk_css_selector_free (selector);
1327 if (_gtk_css_parser_try (parser, "n", TRUE))
1329 if (_gtk_css_parser_try (parser, "+", TRUE))
1331 else if (_gtk_css_parser_try (parser, "-", TRUE))
1336 if (_gtk_css_parser_try_int (parser, &b))
1340 _gtk_css_parser_error (parser, "Expected an integer");
1342 _gtk_css_selector_free (selector);
1358 if (!_gtk_css_parser_try (parser, ")", FALSE))
1360 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1362 _gtk_css_selector_free (selector);
1366 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1368 encode_position (type, a, b));
1373 static GtkCssSelector *
1374 parse_selector_pseudo_class (GtkCssParser *parser,
1375 GtkCssSelector *selector)
1377 static const struct {
1379 GtkStateFlags state_flag;
1380 PositionType position_type;
1383 } pseudo_classes[] = {
1384 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1385 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1386 { "only-child", 0, POSITION_ONLY, 0, 0 },
1387 { "sorted", 0, POSITION_SORTED, 0, 0 },
1388 { "active", GTK_STATE_FLAG_ACTIVE, },
1389 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1390 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1391 { "selected", GTK_STATE_FLAG_SELECTED, },
1392 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1393 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1394 { "focused", GTK_STATE_FLAG_FOCUSED, },
1395 { "focus", GTK_STATE_FLAG_FOCUSED, },
1396 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
1400 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1401 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1402 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1403 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1405 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1407 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1409 if (pseudo_classes[i].state_flag)
1410 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1412 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1414 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1416 encode_position (pseudo_classes[i].position_type,
1417 pseudo_classes[i].position_a,
1418 pseudo_classes[i].position_b));
1423 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1425 _gtk_css_selector_free (selector);
1429 static GtkCssSelector *
1430 try_parse_name (GtkCssParser *parser,
1431 GtkCssSelector *selector)
1435 name = _gtk_css_parser_try_ident (parser, FALSE);
1438 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
1439 ? >K_CSS_SELECTOR_REGION
1440 : >K_CSS_SELECTOR_NAME,
1442 g_intern_string (name));
1445 else if (_gtk_css_parser_try (parser, "*", FALSE))
1446 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1451 static GtkCssSelector *
1452 parse_simple_selector (GtkCssParser *parser,
1453 GtkCssSelector *selector)
1455 guint size = gtk_css_selector_size (selector);
1457 selector = try_parse_name (parser, selector);
1460 if (_gtk_css_parser_try (parser, "#", FALSE))
1461 selector = parse_selector_id (parser, selector);
1462 else if (_gtk_css_parser_try (parser, ".", FALSE))
1463 selector = parse_selector_class (parser, selector);
1464 else if (_gtk_css_parser_try (parser, ":", FALSE))
1465 selector = parse_selector_pseudo_class (parser, selector);
1466 else if (gtk_css_selector_size (selector) == size)
1468 _gtk_css_parser_error (parser, "Expected a valid selector");
1470 _gtk_css_selector_free (selector);
1476 while (selector && !_gtk_css_parser_is_eof (parser));
1478 _gtk_css_parser_skip_whitespace (parser);
1484 _gtk_css_selector_parse (GtkCssParser *parser)
1486 GtkCssSelector *selector = NULL;
1488 while ((selector = parse_simple_selector (parser, selector)) &&
1489 !_gtk_css_parser_is_eof (parser) &&
1490 !_gtk_css_parser_begins_with (parser, ',') &&
1491 !_gtk_css_parser_begins_with (parser, '{'))
1493 if (_gtk_css_parser_try (parser, "+", TRUE))
1494 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1495 else if (_gtk_css_parser_try (parser, "~", TRUE))
1496 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1497 else if (_gtk_css_parser_try (parser, ">", TRUE))
1498 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1500 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1507 _gtk_css_selector_free (GtkCssSelector *selector)
1509 g_return_if_fail (selector != NULL);
1515 _gtk_css_selector_print (const GtkCssSelector *selector,
1518 const GtkCssSelector *previous;
1520 g_return_if_fail (selector != NULL);
1522 previous = gtk_css_selector_previous (selector);
1524 _gtk_css_selector_print (previous, str);
1526 selector->class->print (selector, str);
1530 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1534 g_return_val_if_fail (selector != NULL, NULL);
1536 string = g_string_new (NULL);
1538 _gtk_css_selector_print (selector, string);
1540 return g_string_free (string, FALSE);
1544 _gtk_css_selector_get_change (const GtkCssSelector *selector)
1546 g_return_val_if_fail (selector != NULL, 0);
1548 return gtk_css_selector_get_change (selector);
1552 * _gtk_css_selector_matches:
1553 * @selector: the selector
1554 * @path: the path to check
1555 * @state: The state to match
1557 * Checks if the @selector matches the given @path. If @length is
1558 * smaller than the number of elements in @path, it is assumed that
1559 * only the first @length element of @path are valid and the rest
1560 * does not exist. This is useful for doing parent matches for the
1561 * 'inherit' keyword.
1563 * Returns: %TRUE if the selector matches @path
1566 _gtk_css_selector_matches (const GtkCssSelector *selector,
1567 const GtkCssMatcher *matcher)
1570 g_return_val_if_fail (selector != NULL, FALSE);
1571 g_return_val_if_fail (matcher != NULL, FALSE);
1573 return gtk_css_selector_match (selector, matcher);
1576 /* Computes specificity according to CSS 2.1.
1577 * The arguments must be initialized to 0 */
1579 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1584 for (; selector; selector = gtk_css_selector_previous (selector))
1586 const GtkCssSelectorClass *klass = selector->class;
1588 if (klass->increase_id_specificity)
1590 if (klass->increase_class_specificity)
1592 if (klass->increase_element_specificity)
1598 _gtk_css_selector_compare (const GtkCssSelector *a,
1599 const GtkCssSelector *b)
1601 guint a_ids = 0, a_classes = 0, a_elements = 0;
1602 guint b_ids = 0, b_classes = 0, b_elements = 0;
1605 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1606 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1608 compare = a_ids - b_ids;
1612 compare = a_classes - b_classes;
1616 return a_elements - b_elements;
1620 /******************** SelectorTree handling *****************/
1623 gtk_css_selectors_count_initial_init (void)
1625 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
1629 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
1631 if (!selector->class->is_simple || selector->class->must_keep_order)
1633 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1634 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1639 selector && selector->class->is_simple && !selector->class->must_keep_order;
1640 selector = gtk_css_selector_previous (selector))
1642 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1643 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1648 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
1650 if (!selector->class->is_simple || selector->class->must_keep_order)
1651 return gtk_css_selector_equal (selector, initial);
1654 selector && selector->class->is_simple && !selector->class->must_keep_order;
1655 selector = gtk_css_selector_previous (selector))
1657 if (gtk_css_selector_equal (selector, initial))
1664 static GtkCssSelector *
1665 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
1667 GtkCssSelector *found;
1670 /* If the initial simple selector is not first, move it there so we can skip it
1671 without losing any other selectors */
1672 if (!gtk_css_selector_equal (selector, initial))
1674 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
1676 if (gtk_css_selector_equal (found, initial))
1680 g_assert (found != NULL && found->class->is_simple);
1687 return (GtkCssSelector *)gtk_css_selector_previous (selector);
1691 direct_ptr_compare (const void *_a, const void *_b)
1693 gpointer *a = (gpointer *)_a;
1694 gpointer *b = (gpointer *)_b;
1703 _gtk_css_selector_tree_match_all (GtkCssSelectorTree *tree,
1704 const GtkCssMatcher *matcher)
1708 GHashTableIter iter;
1711 res = g_hash_table_new (g_direct_hash, g_direct_equal);
1713 for (; tree != NULL; tree = tree->siblings)
1714 gtk_css_selector_tree_match (tree, matcher, res);
1716 array = g_ptr_array_sized_new (g_hash_table_size (res));
1718 g_hash_table_iter_init (&iter, res);
1719 while (g_hash_table_iter_next (&iter, &key, NULL))
1720 g_ptr_array_add (array, key);
1722 g_hash_table_destroy (res);
1724 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
1731 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
1733 gboolean first = TRUE;
1736 for (; tree != NULL; tree = tree->siblings, first = FALSE)
1739 g_string_append (str, prefix);
1744 g_string_append (str, "─┬─");
1746 g_string_append (str, "───");
1751 g_string_append (str, " ├─");
1753 g_string_append (str, " └─");
1757 tree->selector.class->print (&tree->selector, str);
1758 len = str->len - len;
1762 GString *prefix2 = g_string_new (prefix);
1765 g_string_append (prefix2, " │ ");
1767 g_string_append (prefix2, " ");
1768 for (i = 0; i < len; i++)
1769 g_string_append_c (prefix2, ' ');
1771 _gtk_css_selector_tree_print (tree->previous, str, prefix2->str);
1772 g_string_free (prefix2, TRUE);
1775 g_string_append (str, "\n");
1781 _gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
1784 g_return_if_fail (tree != NULL);
1786 tree->selector.class->print (&tree->selector, str);
1789 _gtk_css_selector_tree_match_print (tree->parent, str);
1793 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
1798 _gtk_css_selector_tree_free (tree->siblings);
1799 _gtk_css_selector_tree_free (tree->previous);
1807 GtkCssSelector *current_selector;
1808 GtkCssSelectorTree **selector_match;
1809 } GtkCssSelectorRuleSetInfo;
1812 static GtkCssSelectorTree *
1813 subdivide_infos (GList *infos, GtkCssSelectorTree *parent)
1815 GHashTable *ht = gtk_css_selectors_count_initial_init ();
1819 GtkCssSelectorTree *tree;
1820 GtkCssSelectorRuleSetInfo *info;
1821 GtkCssSelector *max_selector;
1822 GHashTableIter iter;
1824 gpointer key, value;
1825 GPtrArray *exact_matches;
1830 for (l = infos; l != NULL; l = l->next)
1833 gtk_css_selectors_count_initial (info->current_selector, ht);
1836 /* Pick the selector with highest count, and use as decision on this level
1837 as that makes it possible to skip the largest amount of checks later */
1840 max_selector = NULL;
1842 g_hash_table_iter_init (&iter, ht);
1843 while (g_hash_table_iter_next (&iter, &key, &value))
1845 GtkCssSelector *selector = key;
1846 if (GPOINTER_TO_UINT (value) > max_count ||
1847 (GPOINTER_TO_UINT (value) == max_count &&
1848 gtk_css_selector_compare_one (selector, max_selector) < 0))
1850 max_count = GPOINTER_TO_UINT (value);
1851 max_selector = selector;
1858 tree = g_new0 (GtkCssSelectorTree, 1);
1859 tree->parent = parent;
1860 tree->selector = *max_selector;
1862 exact_matches = NULL;
1863 for (l = infos; l != NULL; l = l->next)
1867 if (gtk_css_selectors_has_initial_selector (info->current_selector, &tree->selector))
1869 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &tree->selector);
1870 if (info->current_selector == NULL)
1872 /* Matches current node */
1873 if (exact_matches == NULL)
1874 exact_matches = g_ptr_array_new ();
1875 g_ptr_array_add (exact_matches, info->match);
1876 if (info->selector_match != NULL)
1877 *info->selector_match = tree;
1880 matched = g_list_prepend (matched, info);
1884 remaining = g_list_prepend (remaining, info);
1890 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
1891 tree->matches = g_ptr_array_free (exact_matches, FALSE);
1895 tree->previous = subdivide_infos (matched, tree);
1898 tree->siblings = subdivide_infos (remaining, parent);
1903 struct _GtkCssSelectorTreeBuilder {
1907 GtkCssSelectorTreeBuilder *
1908 _gtk_css_selector_tree_builder_new (void)
1910 return g_new0 (GtkCssSelectorTreeBuilder, 1);
1914 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
1916 g_list_free_full (builder->infos, g_free);
1921 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
1922 GtkCssSelector *selectors,
1923 GtkCssSelectorTree **selector_match,
1926 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
1928 info->match = match;
1929 info->current_selector = selectors;
1930 info->selector_match = selector_match;
1931 builder->infos = g_list_prepend (builder->infos, info);
1934 GtkCssSelectorTree *
1935 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
1937 GtkCssSelectorTree *tree;
1939 tree = subdivide_infos (builder->infos, NULL);
1943 GString *s = g_string_new ("");
1944 _gtk_css_selector_tree_print (tree, s, "");
1945 g_print ("%s", s->str);
1946 g_string_free (s, TRUE);