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 GtkCssChange previous_change);
42 int (* compare_one) (const GtkCssSelector *a,
43 const GtkCssSelector *b);
45 guint increase_id_specificity :1;
46 guint increase_class_specificity :1;
47 guint increase_element_specificity :1;
49 guint must_keep_order :1; /* Due to region weirdness these must be kept before a DESCENDANT, so don't reorder */
52 struct _GtkCssSelector
54 const GtkCssSelectorClass *class; /* type of check this selector does */
55 gconstpointer data; /* data for matching:
56 - interned string for CLASS, NAME and ID
57 - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
60 struct _GtkCssSelectorTree
62 GtkCssSelector selector;
63 GtkCssSelectorTree *parent;
64 GtkCssSelectorTree *previous;
65 GtkCssSelectorTree *siblings;
66 gpointer *matches; /* pointers that we return as matches if selector matches */
70 gtk_css_selector_equal (const GtkCssSelector *a,
71 const GtkCssSelector *b)
74 a->class == b->class &&
79 gtk_css_selector_hash (const GtkCssSelector *selector)
81 return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
85 gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
92 for (i = 0; tree->matches[i] != NULL; i++)
93 g_hash_table_insert (res, tree->matches[i], tree->matches[i]);
98 gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
99 const GtkCssMatcher *matcher,
105 tree->selector.class->tree_match (tree, matcher, res);
109 gtk_css_selector_match (const GtkCssSelector *selector,
110 const GtkCssMatcher *matcher)
112 if (selector == NULL)
115 return selector->class->match (selector, matcher);
119 gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
121 if (a->class != b->class)
122 return strcmp (a->class->name, b->class->name);
124 return a->class->compare_one (a, b);
127 static const GtkCssSelector *
128 gtk_css_selector_previous (const GtkCssSelector *selector)
130 selector = selector + 1;
132 return selector->class ? selector : NULL;
138 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
141 g_string_append_c (string, ' ');
145 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
146 const GtkCssMatcher *matcher)
148 GtkCssMatcher ancestor;
150 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
154 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
162 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
163 const GtkCssMatcher *matcher,
166 GtkCssMatcher ancestor;
167 const GtkCssSelectorTree *prev;
169 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
173 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
174 gtk_css_selector_tree_match (prev, matcher, res);
176 /* any matchers are dangerous here, as we may loop forever, but
177 we can terminate now as all possible matches have already been added */
178 if (_gtk_css_matcher_matches_any (matcher))
184 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
185 const GtkCssSelector *b)
191 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
193 return _gtk_css_change_for_child (previous_change);
196 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
198 gtk_css_selector_descendant_print,
199 gtk_css_selector_descendant_match,
200 gtk_css_selector_descendant_tree_match,
201 gtk_css_selector_descendant_get_change,
202 gtk_css_selector_descendant_compare_one,
203 FALSE, FALSE, FALSE, FALSE, FALSE
209 gtk_css_selector_child_print (const GtkCssSelector *selector,
212 g_string_append (string, " > ");
216 gtk_css_selector_child_match (const GtkCssSelector *selector,
217 const GtkCssMatcher *matcher)
219 GtkCssMatcher parent;
221 if (!_gtk_css_matcher_get_parent (&parent, matcher))
224 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
228 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
229 const GtkCssMatcher *matcher,
232 GtkCssMatcher parent;
233 const GtkCssSelectorTree *prev;
235 if (!_gtk_css_matcher_get_parent (&parent, matcher))
238 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
239 gtk_css_selector_tree_match (prev, &parent, res);
243 gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
245 return _gtk_css_change_for_child (previous_change);
249 gtk_css_selector_child_compare_one (const GtkCssSelector *a,
250 const GtkCssSelector *b)
255 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
257 gtk_css_selector_child_print,
258 gtk_css_selector_child_match,
259 gtk_css_selector_child_tree_match,
260 gtk_css_selector_child_get_change,
261 gtk_css_selector_child_compare_one,
262 FALSE, FALSE, FALSE, FALSE, FALSE
268 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
271 g_string_append (string, " ~ ");
275 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
276 const GtkCssMatcher *matcher)
278 GtkCssMatcher previous;
280 while (_gtk_css_matcher_get_previous (&previous, matcher))
284 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
292 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
293 const GtkCssMatcher *matcher,
296 GtkCssMatcher previous;
297 const GtkCssSelectorTree *prev;
299 while (_gtk_css_matcher_get_previous (&previous, matcher))
303 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
304 gtk_css_selector_tree_match (prev, matcher, res);
306 /* any matchers are dangerous here, as we may loop forever, but
307 we can terminate now as all possible matches have already been added */
308 if (_gtk_css_matcher_matches_any (matcher))
314 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
316 return _gtk_css_change_for_sibling (previous_change);
320 gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
321 const GtkCssSelector *b)
327 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
329 gtk_css_selector_sibling_print,
330 gtk_css_selector_sibling_match,
331 gtk_css_selector_sibling_tree_match,
332 gtk_css_selector_sibling_get_change,
333 gtk_css_selector_sibling_compare_one,
334 FALSE, FALSE, FALSE, FALSE, FALSE
340 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
343 g_string_append (string, " + ");
347 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
348 const GtkCssMatcher *matcher)
350 GtkCssMatcher previous;
352 if (!_gtk_css_matcher_get_previous (&previous, matcher))
355 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
359 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
360 const GtkCssMatcher *matcher,
363 GtkCssMatcher previous;
364 const GtkCssSelectorTree *prev;
366 if (!_gtk_css_matcher_get_previous (&previous, matcher))
371 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
372 gtk_css_selector_tree_match (prev, matcher, res);
376 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
378 return _gtk_css_change_for_sibling (previous_change);
382 gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
383 const GtkCssSelector *b)
388 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
390 gtk_css_selector_adjacent_print,
391 gtk_css_selector_adjacent_match,
392 gtk_css_selector_adjacent_tree_match,
393 gtk_css_selector_adjacent_get_change,
394 gtk_css_selector_adjacent_compare_one,
395 FALSE, FALSE, FALSE, FALSE, FALSE
401 gtk_css_selector_any_print (const GtkCssSelector *selector,
404 g_string_append_c (string, '*');
408 gtk_css_selector_any_match (const GtkCssSelector *selector,
409 const GtkCssMatcher *matcher)
411 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
414 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
415 _gtk_css_matcher_has_regions (matcher))
417 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
421 return gtk_css_selector_match (previous, matcher);
425 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
426 const GtkCssMatcher *matcher,
429 const GtkCssSelectorTree *prev, *prev2;
431 gtk_css_selector_tree_found_match (tree, res);
433 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
435 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
436 _gtk_css_matcher_has_regions (matcher))
438 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
439 gtk_css_selector_tree_match (prev2, matcher, res);
442 gtk_css_selector_tree_match (prev, matcher, res);
447 gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
449 return previous_change;
453 gtk_css_selector_any_compare_one (const GtkCssSelector *a,
454 const GtkCssSelector *b)
459 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
461 gtk_css_selector_any_print,
462 gtk_css_selector_any_match,
463 gtk_css_selector_any_tree_match,
464 gtk_css_selector_any_get_change,
465 gtk_css_selector_any_compare_one,
466 FALSE, FALSE, FALSE, TRUE, TRUE
472 gtk_css_selector_name_print (const GtkCssSelector *selector,
475 g_string_append (string, selector->data);
479 gtk_css_selector_name_match (const GtkCssSelector *selector,
480 const GtkCssMatcher *matcher)
482 if (!_gtk_css_matcher_has_name (matcher, selector->data))
485 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
489 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
490 const GtkCssMatcher *matcher,
493 const GtkCssSelectorTree *prev;
495 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
498 gtk_css_selector_tree_found_match (tree, res);
500 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
501 gtk_css_selector_tree_match (prev, matcher, res);
506 gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
508 return previous_change | GTK_CSS_CHANGE_NAME;
512 gtk_css_selector_name_compare_one (const GtkCssSelector *a,
513 const GtkCssSelector *b)
515 return strcmp (a->data, b->data);
518 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
520 gtk_css_selector_name_print,
521 gtk_css_selector_name_match,
522 gtk_css_selector_name_tree_match,
523 gtk_css_selector_name_get_change,
524 gtk_css_selector_name_compare_one,
525 FALSE, FALSE, TRUE, TRUE, FALSE
531 gtk_css_selector_region_print (const GtkCssSelector *selector,
534 g_string_append (string, selector->data);
538 gtk_css_selector_region_match (const GtkCssSelector *selector,
539 const GtkCssMatcher *matcher)
541 const GtkCssSelector *previous;
543 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
546 previous = gtk_css_selector_previous (selector);
547 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
548 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
551 return gtk_css_selector_match (previous, matcher);
555 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
556 const GtkCssMatcher *matcher,
559 const GtkCssSelectorTree *prev, *prev2;
561 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
564 gtk_css_selector_tree_found_match (tree, res);
566 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
568 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
570 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
571 gtk_css_selector_tree_match (prev2, matcher, res);
574 gtk_css_selector_tree_match (prev, matcher, res);
579 gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
583 change = previous_change;
584 change |= GTK_CSS_CHANGE_REGION;
585 change |= _gtk_css_change_for_child (change);
591 gtk_css_selector_region_compare_one (const GtkCssSelector *a,
592 const GtkCssSelector *b)
594 return strcmp (a->data, b->data);
597 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
599 gtk_css_selector_region_print,
600 gtk_css_selector_region_match,
601 gtk_css_selector_region_tree_match,
602 gtk_css_selector_region_get_change,
603 gtk_css_selector_region_compare_one,
604 FALSE, FALSE, TRUE, TRUE, TRUE
610 gtk_css_selector_class_print (const GtkCssSelector *selector,
613 g_string_append_c (string, '.');
614 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
618 gtk_css_selector_class_match (const GtkCssSelector *selector,
619 const GtkCssMatcher *matcher)
621 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
624 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
628 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
629 const GtkCssMatcher *matcher,
632 const GtkCssSelectorTree *prev;
634 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
637 gtk_css_selector_tree_found_match (tree, res);
639 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
640 gtk_css_selector_tree_match (prev, matcher, res);
644 gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
646 return previous_change | GTK_CSS_CHANGE_CLASS;
650 gtk_css_selector_class_compare_one (const GtkCssSelector *a,
651 const GtkCssSelector *b)
653 return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
654 g_quark_to_string (GPOINTER_TO_UINT (b->data)));
657 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
659 gtk_css_selector_class_print,
660 gtk_css_selector_class_match,
661 gtk_css_selector_class_tree_match,
662 gtk_css_selector_class_get_change,
663 gtk_css_selector_class_compare_one,
664 FALSE, TRUE, FALSE, TRUE, FALSE
670 gtk_css_selector_id_print (const GtkCssSelector *selector,
673 g_string_append_c (string, '#');
674 g_string_append (string, selector->data);
678 gtk_css_selector_id_match (const GtkCssSelector *selector,
679 const GtkCssMatcher *matcher)
681 if (!_gtk_css_matcher_has_id (matcher, selector->data))
684 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
688 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
689 const GtkCssMatcher *matcher,
692 const GtkCssSelectorTree *prev;
694 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
697 gtk_css_selector_tree_found_match (tree, res);
699 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
700 gtk_css_selector_tree_match (prev, matcher, res);
704 gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
706 return previous_change | GTK_CSS_CHANGE_ID;
711 gtk_css_selector_id_compare_one (const GtkCssSelector *a,
712 const GtkCssSelector *b)
714 return strcmp (a->data, b->data);
717 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
719 gtk_css_selector_id_print,
720 gtk_css_selector_id_match,
721 gtk_css_selector_id_tree_match,
722 gtk_css_selector_id_get_change,
723 gtk_css_selector_id_compare_one,
724 TRUE, FALSE, FALSE, TRUE, FALSE
727 /* PSEUDOCLASS FOR STATE */
730 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
733 static const char * state_names[] = {
744 state = GPOINTER_TO_UINT (selector->data);
745 g_string_append_c (string, ':');
747 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
749 if (state == (1 << i))
751 g_string_append (string, state_names[i]);
756 g_assert_not_reached ();
760 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
761 const GtkCssMatcher *matcher)
763 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
765 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
768 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
772 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
773 const GtkCssMatcher *matcher,
776 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
777 const GtkCssSelectorTree *prev;
779 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
782 gtk_css_selector_tree_found_match (tree, res);
784 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
785 gtk_css_selector_tree_match (prev, matcher, res);
790 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
792 return previous_change | GTK_CSS_CHANGE_STATE;
796 gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
797 const GtkCssSelector *b)
799 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
802 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
804 gtk_css_selector_pseudoclass_state_print,
805 gtk_css_selector_pseudoclass_state_match,
806 gtk_css_selector_pseudoclass_state_tree_match,
807 gtk_css_selector_pseudoclass_state_get_change,
808 gtk_css_selector_pseudoclass_state_compare_one,
809 FALSE, TRUE, FALSE, TRUE, FALSE
812 /* PSEUDOCLASS FOR POSITION */
820 #define POSITION_TYPE_BITS 2
821 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
824 encode_position (PositionType type,
831 gssize type :POSITION_TYPE_BITS;
832 gssize a :POSITION_NUMBER_BITS;
833 gssize b :POSITION_NUMBER_BITS;
836 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
838 g_assert (type < (1 << POSITION_TYPE_BITS));
840 result.data.type = type;
848 decode_position (const GtkCssSelector *selector,
856 gssize type :POSITION_TYPE_BITS;
857 gssize a :POSITION_NUMBER_BITS;
858 gssize b :POSITION_NUMBER_BITS;
861 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
863 result.p = selector->data;
865 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
871 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
877 decode_position (selector, &type, &a, &b);
880 case POSITION_FORWARD:
884 g_string_append (string, ":first-child");
886 g_string_append_printf (string, ":nth-child(%d)", b);
888 else if (a == 2 && b == 0)
889 g_string_append (string, ":nth-child(even)");
890 else if (a == 2 && b == 1)
891 g_string_append (string, ":nth-child(odd)");
894 g_string_append (string, ":nth-child(");
896 g_string_append (string, "n");
898 g_string_append (string, "-n");
900 g_string_append_printf (string, "%dn", a);
902 g_string_append_printf (string, "+%d)", b);
904 g_string_append_printf (string, "%d)", b);
906 g_string_append (string, ")");
909 case POSITION_BACKWARD:
913 g_string_append (string, ":last-child");
915 g_string_append_printf (string, ":nth-last-child(%d)", b);
917 else if (a == 2 && b == 0)
918 g_string_append (string, ":nth-last-child(even)");
919 else if (a == 2 && b == 1)
920 g_string_append (string, ":nth-last-child(odd)");
923 g_string_append (string, ":nth-last-child(");
925 g_string_append (string, "n");
927 g_string_append (string, "-n");
929 g_string_append_printf (string, "%dn", a);
931 g_string_append_printf (string, "+%d)", b);
933 g_string_append_printf (string, "%d)", b);
935 g_string_append (string, ")");
939 g_string_append (string, ":only-child");
941 case POSITION_SORTED:
942 g_string_append (string, ":sorted");
945 g_assert_not_reached ();
951 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
952 const GtkCssMatcher *matcher)
954 GtkRegionFlags selector_flags;
955 const GtkCssSelector *previous;
959 decode_position (selector, &type, &a, &b);
962 case POSITION_FORWARD:
963 if (a == 0 && b == 1)
964 selector_flags = GTK_REGION_FIRST;
965 else if (a == 2 && b == 0)
966 selector_flags = GTK_REGION_EVEN;
967 else if (a == 2 && b == 1)
968 selector_flags = GTK_REGION_ODD;
972 case POSITION_BACKWARD:
973 if (a == 0 && b == 1)
974 selector_flags = GTK_REGION_LAST;
979 selector_flags = GTK_REGION_ONLY;
981 case POSITION_SORTED:
982 selector_flags = GTK_REGION_SORTED;
985 g_assert_not_reached ();
988 selector = gtk_css_selector_previous (selector);
990 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
993 previous = gtk_css_selector_previous (selector);
994 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
995 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
998 return gtk_css_selector_match (previous, matcher);
1002 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
1003 const GtkCssMatcher *matcher)
1005 const GtkCssSelector *previous;
1009 previous = gtk_css_selector_previous (selector);
1010 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
1011 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
1013 decode_position (selector, &type, &a, &b);
1016 case POSITION_FORWARD:
1017 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1020 case POSITION_BACKWARD:
1021 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1025 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1026 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1029 case POSITION_SORTED:
1032 g_assert_not_reached ();
1036 return gtk_css_selector_match (previous, matcher);
1040 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
1041 const GtkCssSelectorTree *prev,
1042 const GtkCssMatcher *matcher,
1045 const GtkCssSelectorTree *prev2;
1046 GtkRegionFlags selector_flags;
1050 decode_position (&tree->selector, &type, &a, &b);
1053 case POSITION_FORWARD:
1054 if (a == 0 && b == 1)
1055 selector_flags = GTK_REGION_FIRST;
1056 else if (a == 2 && b == 0)
1057 selector_flags = GTK_REGION_EVEN;
1058 else if (a == 2 && b == 1)
1059 selector_flags = GTK_REGION_ODD;
1063 case POSITION_BACKWARD:
1064 if (a == 0 && b == 1)
1065 selector_flags = GTK_REGION_LAST;
1070 selector_flags = GTK_REGION_ONLY;
1072 case POSITION_SORTED:
1073 selector_flags = GTK_REGION_SORTED;
1076 g_assert_not_reached ();
1080 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1083 gtk_css_selector_tree_found_match (prev, res);
1085 for (prev2 = prev->previous; prev2 != NULL; prev2 = prev2->siblings)
1087 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1088 gtk_css_selector_tree_match (prev2->previous, matcher, res);
1089 gtk_css_selector_tree_match (prev2, matcher, res);
1094 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1095 const GtkCssMatcher *matcher,
1098 const GtkCssSelectorTree *prev;
1102 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
1104 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1105 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1108 decode_position (&tree->selector, &type, &a, &b);
1111 case POSITION_FORWARD:
1112 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1115 case POSITION_BACKWARD:
1116 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1120 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1121 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1124 case POSITION_SORTED:
1127 g_assert_not_reached ();
1131 gtk_css_selector_tree_found_match (tree, res);
1133 for (prev = tree->previous; prev != NULL; prev = prev->siblings)
1135 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1136 gtk_css_selector_tree_match (prev, matcher, res);
1141 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1143 return previous_change | GTK_CSS_CHANGE_POSITION;
1147 gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
1148 const GtkCssSelector *b)
1150 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1153 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1154 "pseudoclass-position",
1155 gtk_css_selector_pseudoclass_position_print,
1156 gtk_css_selector_pseudoclass_position_match,
1157 gtk_css_selector_pseudoclass_position_tree_match,
1158 gtk_css_selector_pseudoclass_position_get_change,
1159 gtk_css_selector_pseudoclass_position_compare_one,
1160 FALSE, TRUE, FALSE, TRUE, TRUE
1166 gtk_css_selector_size (const GtkCssSelector *selector)
1172 selector = gtk_css_selector_previous (selector);
1179 static GtkCssSelector *
1180 gtk_css_selector_new (const GtkCssSelectorClass *class,
1181 GtkCssSelector *selector,
1186 size = gtk_css_selector_size (selector);
1187 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1189 selector[1].class = NULL;
1191 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1193 selector->class = class;
1194 selector->data = data;
1199 static GtkCssSelector *
1200 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1204 name = _gtk_css_parser_try_name (parser, FALSE);
1208 _gtk_css_parser_error (parser, "Expected a valid name for class");
1210 _gtk_css_selector_free (selector);
1214 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1216 GUINT_TO_POINTER (g_quark_from_string (name)));
1223 static GtkCssSelector *
1224 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1228 name = _gtk_css_parser_try_name (parser, FALSE);
1232 _gtk_css_parser_error (parser, "Expected a valid name for id");
1234 _gtk_css_selector_free (selector);
1238 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1240 g_intern_string (name));
1247 static GtkCssSelector *
1248 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1249 GtkCssSelector *selector,
1254 if (!_gtk_css_parser_try (parser, "(", TRUE))
1256 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1258 _gtk_css_selector_free (selector);
1262 if (_gtk_css_parser_try (parser, "even", TRUE))
1267 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1272 else if (type == POSITION_FORWARD &&
1273 _gtk_css_parser_try (parser, "first", TRUE))
1278 else if (type == POSITION_FORWARD &&
1279 _gtk_css_parser_try (parser, "last", TRUE))
1283 type = POSITION_BACKWARD;
1289 if (_gtk_css_parser_try (parser, "+", TRUE))
1291 else if (_gtk_css_parser_try (parser, "-", TRUE))
1296 if (_gtk_css_parser_try_int (parser, &a))
1300 _gtk_css_parser_error (parser, "Expected an integer");
1302 _gtk_css_selector_free (selector);
1307 else if (_gtk_css_parser_has_prefix (parser, "n"))
1313 _gtk_css_parser_error (parser, "Expected an integer");
1315 _gtk_css_selector_free (selector);
1319 if (_gtk_css_parser_try (parser, "n", TRUE))
1321 if (_gtk_css_parser_try (parser, "+", TRUE))
1323 else if (_gtk_css_parser_try (parser, "-", TRUE))
1328 if (_gtk_css_parser_try_int (parser, &b))
1332 _gtk_css_parser_error (parser, "Expected an integer");
1334 _gtk_css_selector_free (selector);
1350 if (!_gtk_css_parser_try (parser, ")", FALSE))
1352 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1354 _gtk_css_selector_free (selector);
1358 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1360 encode_position (type, a, b));
1365 static GtkCssSelector *
1366 parse_selector_pseudo_class (GtkCssParser *parser,
1367 GtkCssSelector *selector)
1369 static const struct {
1371 GtkStateFlags state_flag;
1372 PositionType position_type;
1375 } pseudo_classes[] = {
1376 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1377 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1378 { "only-child", 0, POSITION_ONLY, 0, 0 },
1379 { "sorted", 0, POSITION_SORTED, 0, 0 },
1380 { "active", GTK_STATE_FLAG_ACTIVE, },
1381 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1382 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1383 { "selected", GTK_STATE_FLAG_SELECTED, },
1384 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1385 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1386 { "focused", GTK_STATE_FLAG_FOCUSED, },
1387 { "focus", GTK_STATE_FLAG_FOCUSED, },
1388 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
1392 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1393 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1394 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1395 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1397 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1399 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1401 if (pseudo_classes[i].state_flag)
1402 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1404 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1406 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1408 encode_position (pseudo_classes[i].position_type,
1409 pseudo_classes[i].position_a,
1410 pseudo_classes[i].position_b));
1415 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1417 _gtk_css_selector_free (selector);
1421 static GtkCssSelector *
1422 try_parse_name (GtkCssParser *parser,
1423 GtkCssSelector *selector)
1427 name = _gtk_css_parser_try_ident (parser, FALSE);
1430 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
1431 ? >K_CSS_SELECTOR_REGION
1432 : >K_CSS_SELECTOR_NAME,
1434 g_intern_string (name));
1437 else if (_gtk_css_parser_try (parser, "*", FALSE))
1438 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1443 static GtkCssSelector *
1444 parse_simple_selector (GtkCssParser *parser,
1445 GtkCssSelector *selector)
1447 guint size = gtk_css_selector_size (selector);
1449 selector = try_parse_name (parser, selector);
1452 if (_gtk_css_parser_try (parser, "#", FALSE))
1453 selector = parse_selector_id (parser, selector);
1454 else if (_gtk_css_parser_try (parser, ".", FALSE))
1455 selector = parse_selector_class (parser, selector);
1456 else if (_gtk_css_parser_try (parser, ":", FALSE))
1457 selector = parse_selector_pseudo_class (parser, selector);
1458 else if (gtk_css_selector_size (selector) == size)
1460 _gtk_css_parser_error (parser, "Expected a valid selector");
1462 _gtk_css_selector_free (selector);
1468 while (selector && !_gtk_css_parser_is_eof (parser));
1470 _gtk_css_parser_skip_whitespace (parser);
1476 _gtk_css_selector_parse (GtkCssParser *parser)
1478 GtkCssSelector *selector = NULL;
1480 while ((selector = parse_simple_selector (parser, selector)) &&
1481 !_gtk_css_parser_is_eof (parser) &&
1482 !_gtk_css_parser_begins_with (parser, ',') &&
1483 !_gtk_css_parser_begins_with (parser, '{'))
1485 if (_gtk_css_parser_try (parser, "+", TRUE))
1486 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1487 else if (_gtk_css_parser_try (parser, "~", TRUE))
1488 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1489 else if (_gtk_css_parser_try (parser, ">", TRUE))
1490 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1492 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1499 _gtk_css_selector_free (GtkCssSelector *selector)
1501 g_return_if_fail (selector != NULL);
1507 _gtk_css_selector_print (const GtkCssSelector *selector,
1510 const GtkCssSelector *previous;
1512 g_return_if_fail (selector != NULL);
1514 previous = gtk_css_selector_previous (selector);
1516 _gtk_css_selector_print (previous, str);
1518 selector->class->print (selector, str);
1522 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1526 g_return_val_if_fail (selector != NULL, NULL);
1528 string = g_string_new (NULL);
1530 _gtk_css_selector_print (selector, string);
1532 return g_string_free (string, FALSE);
1537 _gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree)
1539 GtkCssChange change = 0;
1543 change = tree->selector.class->get_change (&tree->selector, change);
1544 tree = tree->parent;
1551 * _gtk_css_selector_matches:
1552 * @selector: the selector
1553 * @path: the path to check
1554 * @state: The state to match
1556 * Checks if the @selector matches the given @path. If @length is
1557 * smaller than the number of elements in @path, it is assumed that
1558 * only the first @length element of @path are valid and the rest
1559 * does not exist. This is useful for doing parent matches for the
1560 * 'inherit' keyword.
1562 * Returns: %TRUE if the selector matches @path
1565 _gtk_css_selector_matches (const GtkCssSelector *selector,
1566 const GtkCssMatcher *matcher)
1569 g_return_val_if_fail (selector != NULL, FALSE);
1570 g_return_val_if_fail (matcher != NULL, FALSE);
1572 return gtk_css_selector_match (selector, matcher);
1575 /* Computes specificity according to CSS 2.1.
1576 * The arguments must be initialized to 0 */
1578 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1583 for (; selector; selector = gtk_css_selector_previous (selector))
1585 const GtkCssSelectorClass *klass = selector->class;
1587 if (klass->increase_id_specificity)
1589 if (klass->increase_class_specificity)
1591 if (klass->increase_element_specificity)
1597 _gtk_css_selector_compare (const GtkCssSelector *a,
1598 const GtkCssSelector *b)
1600 guint a_ids = 0, a_classes = 0, a_elements = 0;
1601 guint b_ids = 0, b_classes = 0, b_elements = 0;
1604 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1605 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1607 compare = a_ids - b_ids;
1611 compare = a_classes - b_classes;
1615 return a_elements - b_elements;
1619 /******************** SelectorTree handling *****************/
1622 gtk_css_selectors_count_initial_init (void)
1624 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
1628 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
1630 if (!selector->class->is_simple || selector->class->must_keep_order)
1632 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1633 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1638 selector && selector->class->is_simple && !selector->class->must_keep_order;
1639 selector = gtk_css_selector_previous (selector))
1641 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1642 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1647 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
1649 if (!selector->class->is_simple || selector->class->must_keep_order)
1650 return gtk_css_selector_equal (selector, initial);
1653 selector && selector->class->is_simple && !selector->class->must_keep_order;
1654 selector = gtk_css_selector_previous (selector))
1656 if (gtk_css_selector_equal (selector, initial))
1663 static GtkCssSelector *
1664 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
1666 GtkCssSelector *found;
1669 /* If the initial simple selector is not first, move it there so we can skip it
1670 without losing any other selectors */
1671 if (!gtk_css_selector_equal (selector, initial))
1673 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
1675 if (gtk_css_selector_equal (found, initial))
1679 g_assert (found != NULL && found->class->is_simple);
1686 return (GtkCssSelector *)gtk_css_selector_previous (selector);
1690 direct_ptr_compare (const void *_a, const void *_b)
1692 gpointer *a = (gpointer *)_a;
1693 gpointer *b = (gpointer *)_b;
1702 _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
1703 const GtkCssMatcher *matcher)
1707 GHashTableIter iter;
1710 res = g_hash_table_new (g_direct_hash, g_direct_equal);
1712 for (; tree != NULL; tree = tree->siblings)
1713 gtk_css_selector_tree_match (tree, matcher, res);
1715 array = g_ptr_array_sized_new (g_hash_table_size (res));
1717 g_hash_table_iter_init (&iter, res);
1718 while (g_hash_table_iter_next (&iter, &key, NULL))
1719 g_ptr_array_add (array, key);
1721 g_hash_table_destroy (res);
1723 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
1730 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
1732 gboolean first = TRUE;
1735 for (; tree != NULL; tree = tree->siblings, first = FALSE)
1738 g_string_append (str, prefix);
1743 g_string_append (str, "─┬─");
1745 g_string_append (str, "───");
1750 g_string_append (str, " ├─");
1752 g_string_append (str, " └─");
1756 tree->selector.class->print (&tree->selector, str);
1757 len = str->len - len;
1761 GString *prefix2 = g_string_new (prefix);
1764 g_string_append (prefix2, " │ ");
1766 g_string_append (prefix2, " ");
1767 for (i = 0; i < len; i++)
1768 g_string_append_c (prefix2, ' ');
1770 _gtk_css_selector_tree_print (tree->previous, str, prefix2->str);
1771 g_string_free (prefix2, TRUE);
1774 g_string_append (str, "\n");
1780 _gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
1783 g_return_if_fail (tree != NULL);
1785 tree->selector.class->print (&tree->selector, str);
1788 _gtk_css_selector_tree_match_print (tree->parent, str);
1792 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
1797 _gtk_css_selector_tree_free (tree->siblings);
1798 _gtk_css_selector_tree_free (tree->previous);
1806 GtkCssSelector *current_selector;
1807 GtkCssSelectorTree **selector_match;
1808 } GtkCssSelectorRuleSetInfo;
1811 static GtkCssSelectorTree *
1812 subdivide_infos (GList *infos, GtkCssSelectorTree *parent)
1814 GHashTable *ht = gtk_css_selectors_count_initial_init ();
1818 GtkCssSelectorTree *tree;
1819 GtkCssSelectorRuleSetInfo *info;
1820 GtkCssSelector *max_selector;
1821 GHashTableIter iter;
1823 gpointer key, value;
1824 GPtrArray *exact_matches;
1829 for (l = infos; l != NULL; l = l->next)
1832 gtk_css_selectors_count_initial (info->current_selector, ht);
1835 /* Pick the selector with highest count, and use as decision on this level
1836 as that makes it possible to skip the largest amount of checks later */
1839 max_selector = NULL;
1841 g_hash_table_iter_init (&iter, ht);
1842 while (g_hash_table_iter_next (&iter, &key, &value))
1844 GtkCssSelector *selector = key;
1845 if (GPOINTER_TO_UINT (value) > max_count ||
1846 (GPOINTER_TO_UINT (value) == max_count &&
1847 gtk_css_selector_compare_one (selector, max_selector) < 0))
1849 max_count = GPOINTER_TO_UINT (value);
1850 max_selector = selector;
1857 tree = g_new0 (GtkCssSelectorTree, 1);
1858 tree->parent = parent;
1859 tree->selector = *max_selector;
1861 exact_matches = NULL;
1862 for (l = infos; l != NULL; l = l->next)
1866 if (gtk_css_selectors_has_initial_selector (info->current_selector, &tree->selector))
1868 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &tree->selector);
1869 if (info->current_selector == NULL)
1871 /* Matches current node */
1872 if (exact_matches == NULL)
1873 exact_matches = g_ptr_array_new ();
1874 g_ptr_array_add (exact_matches, info->match);
1875 if (info->selector_match != NULL)
1876 *info->selector_match = tree;
1879 matched = g_list_prepend (matched, info);
1883 remaining = g_list_prepend (remaining, info);
1889 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
1890 tree->matches = g_ptr_array_free (exact_matches, FALSE);
1894 tree->previous = subdivide_infos (matched, tree);
1897 tree->siblings = subdivide_infos (remaining, parent);
1902 struct _GtkCssSelectorTreeBuilder {
1906 GtkCssSelectorTreeBuilder *
1907 _gtk_css_selector_tree_builder_new (void)
1909 return g_new0 (GtkCssSelectorTreeBuilder, 1);
1913 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
1915 g_list_free_full (builder->infos, g_free);
1920 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
1921 GtkCssSelector *selectors,
1922 GtkCssSelectorTree **selector_match,
1925 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
1927 info->match = match;
1928 info->current_selector = selectors;
1929 info->selector_match = selector_match;
1930 builder->infos = g_list_prepend (builder->infos, info);
1933 GtkCssSelectorTree *
1934 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
1936 GtkCssSelectorTree *tree;
1938 tree = subdivide_infos (builder->infos, NULL);
1942 GString *s = g_string_new ("");
1943 _gtk_css_selector_tree_print (tree, s, "");
1944 g_print ("%s", s->str);
1945 g_string_free (s, TRUE);