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"
24 #include "gtkcssprovider.h"
25 #include "gtkstylecontextprivate.h"
27 typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
29 struct _GtkCssSelectorClass {
32 void (* print) (const GtkCssSelector *selector,
34 gboolean (* match) (const GtkCssSelector *selector,
35 const GtkCssMatcher *matcher);
36 GtkCssChange (* get_change) (const GtkCssSelector *selector);
38 guint increase_id_specificity :1;
39 guint increase_class_specificity :1;
40 guint increase_element_specificity :1;
44 struct _GtkCssSelector
46 const GtkCssSelectorClass *class; /* type of check this selector does */
47 gconstpointer data; /* data for matching:
48 - interned string for CLASS, NAME and ID
49 - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
53 gtk_css_selector_match (const GtkCssSelector *selector,
54 const GtkCssMatcher *matcher)
59 return selector->class->match (selector, matcher);
63 gtk_css_selector_get_change (const GtkCssSelector *selector)
68 return selector->class->get_change (selector);
71 static const GtkCssSelector *
72 gtk_css_selector_previous (const GtkCssSelector *selector)
74 selector = selector + 1;
76 return selector->class ? selector : NULL;
82 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
85 g_string_append_c (string, ' ');
89 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
90 const GtkCssMatcher *matcher)
92 GtkCssMatcher ancestor;
94 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
98 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
106 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector)
108 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
111 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
113 gtk_css_selector_descendant_print,
114 gtk_css_selector_descendant_match,
115 gtk_css_selector_descendant_get_change,
116 FALSE, FALSE, FALSE, FALSE
122 gtk_css_selector_child_print (const GtkCssSelector *selector,
125 g_string_append (string, " > ");
129 gtk_css_selector_child_match (const GtkCssSelector *selector,
130 const GtkCssMatcher *matcher)
132 GtkCssMatcher parent;
134 if (!_gtk_css_matcher_get_parent (&parent, matcher))
137 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
141 gtk_css_selector_child_get_change (const GtkCssSelector *selector)
143 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
146 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
148 gtk_css_selector_child_print,
149 gtk_css_selector_child_match,
150 gtk_css_selector_child_get_change,
151 FALSE, FALSE, FALSE, FALSE
157 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
160 g_string_append (string, " ~ ");
164 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
165 const GtkCssMatcher *matcher)
167 GtkCssMatcher previous;
169 while (_gtk_css_matcher_get_previous (&previous, matcher))
173 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
181 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector)
183 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
186 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
188 gtk_css_selector_sibling_print,
189 gtk_css_selector_sibling_match,
190 gtk_css_selector_sibling_get_change,
191 FALSE, FALSE, FALSE, FALSE
197 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
200 g_string_append (string, " + ");
204 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
205 const GtkCssMatcher *matcher)
207 GtkCssMatcher previous;
209 if (!_gtk_css_matcher_get_previous (&previous, matcher))
212 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
216 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector)
218 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
221 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
223 gtk_css_selector_adjacent_print,
224 gtk_css_selector_adjacent_match,
225 gtk_css_selector_adjacent_get_change,
226 FALSE, FALSE, FALSE, FALSE
232 gtk_css_selector_any_print (const GtkCssSelector *selector,
235 g_string_append_c (string, '*');
239 gtk_css_selector_any_match (const GtkCssSelector *selector,
240 const GtkCssMatcher *matcher)
242 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
245 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
246 _gtk_css_matcher_has_regions (matcher))
248 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
252 return gtk_css_selector_match (previous, matcher);
256 gtk_css_selector_any_get_change (const GtkCssSelector *selector)
258 return gtk_css_selector_get_change (gtk_css_selector_previous (selector));
261 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
263 gtk_css_selector_any_print,
264 gtk_css_selector_any_match,
265 gtk_css_selector_any_get_change,
266 FALSE, FALSE, FALSE, TRUE
272 gtk_css_selector_name_print (const GtkCssSelector *selector,
275 g_string_append (string, selector->data);
279 gtk_css_selector_name_match (const GtkCssSelector *selector,
280 const GtkCssMatcher *matcher)
282 if (!_gtk_css_matcher_has_name (matcher, selector->data))
285 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
289 gtk_css_selector_name_get_change (const GtkCssSelector *selector)
291 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_NAME;
294 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
296 gtk_css_selector_name_print,
297 gtk_css_selector_name_match,
298 gtk_css_selector_name_get_change,
299 FALSE, FALSE, TRUE, TRUE
305 gtk_css_selector_region_print (const GtkCssSelector *selector,
308 g_string_append (string, selector->data);
312 gtk_css_selector_region_match (const GtkCssSelector *selector,
313 const GtkCssMatcher *matcher)
315 const GtkCssSelector *previous;
317 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
320 previous = gtk_css_selector_previous (selector);
321 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
322 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
325 return gtk_css_selector_match (previous, matcher);
329 gtk_css_selector_region_get_change (const GtkCssSelector *selector)
333 change = gtk_css_selector_get_change (gtk_css_selector_previous (selector));
334 change |= GTK_CSS_CHANGE_REGION;
335 change |= _gtk_css_change_for_child (change);
340 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
342 gtk_css_selector_region_print,
343 gtk_css_selector_region_match,
344 gtk_css_selector_region_get_change,
345 FALSE, FALSE, TRUE, TRUE
351 gtk_css_selector_class_print (const GtkCssSelector *selector,
354 g_string_append_c (string, '.');
355 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
359 gtk_css_selector_class_match (const GtkCssSelector *selector,
360 const GtkCssMatcher *matcher)
362 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
365 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
369 gtk_css_selector_class_get_change (const GtkCssSelector *selector)
371 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_CLASS;
374 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
376 gtk_css_selector_class_print,
377 gtk_css_selector_class_match,
378 gtk_css_selector_class_get_change,
379 FALSE, TRUE, FALSE, TRUE
385 gtk_css_selector_id_print (const GtkCssSelector *selector,
388 g_string_append_c (string, '#');
389 g_string_append (string, selector->data);
393 gtk_css_selector_id_match (const GtkCssSelector *selector,
394 const GtkCssMatcher *matcher)
396 if (!_gtk_css_matcher_has_id (matcher, selector->data))
399 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
403 gtk_css_selector_id_get_change (const GtkCssSelector *selector)
405 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_ID;
408 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
410 gtk_css_selector_id_print,
411 gtk_css_selector_id_match,
412 gtk_css_selector_id_get_change,
413 TRUE, FALSE, FALSE, TRUE
416 /* PSEUDOCLASS FOR STATE */
419 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
422 static const char * state_names[] = {
433 state = GPOINTER_TO_UINT (selector->data);
434 g_string_append_c (string, ':');
436 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
438 if (state == (1 << i))
440 g_string_append (string, state_names[i]);
445 g_assert_not_reached ();
449 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
450 const GtkCssMatcher *matcher)
452 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
454 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
457 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
461 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector)
463 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_STATE;
466 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
468 gtk_css_selector_pseudoclass_state_print,
469 gtk_css_selector_pseudoclass_state_match,
470 gtk_css_selector_pseudoclass_state_get_change,
471 FALSE, TRUE, FALSE, TRUE
474 /* PSEUDOCLASS FOR POSITION */
482 #define POSITION_TYPE_BITS 2
483 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
486 encode_position (PositionType type,
493 gssize type :POSITION_TYPE_BITS;
494 gssize a :POSITION_NUMBER_BITS;
495 gssize b :POSITION_NUMBER_BITS;
498 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
500 g_assert (type < (1 << POSITION_TYPE_BITS));
502 result.data.type = type;
510 decode_position (const GtkCssSelector *selector,
518 gssize type :POSITION_TYPE_BITS;
519 gssize a :POSITION_NUMBER_BITS;
520 gssize b :POSITION_NUMBER_BITS;
523 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
525 result.p = selector->data;
527 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
533 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
539 decode_position (selector, &type, &a, &b);
542 case POSITION_FORWARD:
546 g_string_append (string, ":first-child");
548 g_string_append_printf (string, ":nth-child(%d)", b);
550 else if (a == 2 && b == 0)
551 g_string_append (string, ":nth-child(even)");
552 else if (a == 2 && b == 1)
553 g_string_append (string, ":nth-child(odd)");
556 g_string_append (string, ":nth-child(");
558 g_string_append (string, "n");
560 g_string_append (string, "-n");
562 g_string_append_printf (string, "%dn", a);
564 g_string_append_printf (string, "+%d)", b);
566 g_string_append_printf (string, "%d)", b);
568 g_string_append (string, ")");
571 case POSITION_BACKWARD:
575 g_string_append (string, ":last-child");
577 g_string_append_printf (string, ":nth-last-child(%d)", b);
579 else if (a == 2 && b == 0)
580 g_string_append (string, ":nth-last-child(even)");
581 else if (a == 2 && b == 1)
582 g_string_append (string, ":nth-last-child(odd)");
585 g_string_append (string, ":nth-last-child(");
587 g_string_append (string, "n");
589 g_string_append (string, "-n");
591 g_string_append_printf (string, "%dn", a);
593 g_string_append_printf (string, "+%d)", b);
595 g_string_append_printf (string, "%d)", b);
597 g_string_append (string, ")");
601 g_string_append (string, ":only-child");
603 case POSITION_SORTED:
604 g_string_append (string, ":sorted");
607 g_assert_not_reached ();
613 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
614 const GtkCssMatcher *matcher)
616 GtkRegionFlags selector_flags;
617 const GtkCssSelector *previous;
621 decode_position (selector, &type, &a, &b);
624 case POSITION_FORWARD:
625 if (a == 0 && b == 1)
626 selector_flags = GTK_REGION_FIRST;
627 else if (a == 2 && b == 0)
628 selector_flags = GTK_REGION_EVEN;
629 else if (a == 2 && b == 1)
630 selector_flags = GTK_REGION_ODD;
634 case POSITION_BACKWARD:
635 if (a == 0 && b == 1)
636 selector_flags = GTK_REGION_LAST;
641 selector_flags = GTK_REGION_ONLY;
643 case POSITION_SORTED:
644 selector_flags = GTK_REGION_SORTED;
647 g_assert_not_reached ();
650 selector = gtk_css_selector_previous (selector);
652 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
655 previous = gtk_css_selector_previous (selector);
656 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
657 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
660 return gtk_css_selector_match (previous, matcher);
664 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
665 const GtkCssMatcher *matcher)
667 const GtkCssSelector *previous;
671 previous = gtk_css_selector_previous (selector);
672 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
673 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
675 decode_position (selector, &type, &a, &b);
678 case POSITION_FORWARD:
679 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
682 case POSITION_BACKWARD:
683 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
687 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
688 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
691 case POSITION_SORTED:
694 g_assert_not_reached ();
698 return gtk_css_selector_match (previous, matcher);
702 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector)
704 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_POSITION;
707 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
708 "pseudoclass-position",
709 gtk_css_selector_pseudoclass_position_print,
710 gtk_css_selector_pseudoclass_position_match,
711 gtk_css_selector_pseudoclass_position_get_change,
712 FALSE, TRUE, FALSE, TRUE
718 gtk_css_selector_size (const GtkCssSelector *selector)
724 selector = gtk_css_selector_previous (selector);
731 static GtkCssSelector *
732 gtk_css_selector_new (const GtkCssSelectorClass *class,
733 GtkCssSelector *selector,
738 size = gtk_css_selector_size (selector);
739 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
741 selector[1].class = NULL;
743 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
745 selector->class = class;
746 selector->data = data;
751 static GtkCssSelector *
752 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
756 name = _gtk_css_parser_try_name (parser, FALSE);
760 _gtk_css_parser_error (parser, "Expected a valid name for class");
762 _gtk_css_selector_free (selector);
766 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
768 GUINT_TO_POINTER (g_quark_from_string (name)));
775 static GtkCssSelector *
776 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
780 name = _gtk_css_parser_try_name (parser, FALSE);
784 _gtk_css_parser_error (parser, "Expected a valid name for id");
786 _gtk_css_selector_free (selector);
790 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
792 g_intern_string (name));
799 static GtkCssSelector *
800 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
801 GtkCssSelector *selector,
806 if (!_gtk_css_parser_try (parser, "(", TRUE))
808 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
810 _gtk_css_selector_free (selector);
814 if (_gtk_css_parser_try (parser, "even", TRUE))
819 else if (_gtk_css_parser_try (parser, "odd", TRUE))
824 else if (type == POSITION_FORWARD &&
825 _gtk_css_parser_try (parser, "first", TRUE))
830 else if (type == POSITION_FORWARD &&
831 _gtk_css_parser_try (parser, "last", TRUE))
835 type = POSITION_BACKWARD;
841 if (_gtk_css_parser_try (parser, "+", TRUE))
843 else if (_gtk_css_parser_try (parser, "-", TRUE))
848 if (_gtk_css_parser_try_int (parser, &a))
852 _gtk_css_parser_error (parser, "Expected an integer");
854 _gtk_css_selector_free (selector);
859 else if (_gtk_css_parser_has_prefix (parser, "n"))
865 _gtk_css_parser_error (parser, "Expected an integer");
867 _gtk_css_selector_free (selector);
871 if (_gtk_css_parser_try (parser, "n", TRUE))
873 if (_gtk_css_parser_try (parser, "+", TRUE))
875 else if (_gtk_css_parser_try (parser, "-", TRUE))
880 if (_gtk_css_parser_try_int (parser, &b))
884 _gtk_css_parser_error (parser, "Expected an integer");
886 _gtk_css_selector_free (selector);
902 if (!_gtk_css_parser_try (parser, ")", FALSE))
904 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
906 _gtk_css_selector_free (selector);
910 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
912 encode_position (type, a, b));
917 static GtkCssSelector *
918 parse_selector_pseudo_class (GtkCssParser *parser,
919 GtkCssSelector *selector)
921 static const struct {
923 GtkStateFlags state_flag;
924 PositionType position_type;
927 } pseudo_classes[] = {
928 { "first-child", 0, POSITION_FORWARD, 0, 1 },
929 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
930 { "only-child", 0, POSITION_ONLY, 0, 0 },
931 { "sorted", 0, POSITION_SORTED, 0, 0 },
932 { "active", GTK_STATE_FLAG_ACTIVE, },
933 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
934 { "hover", GTK_STATE_FLAG_PRELIGHT, },
935 { "selected", GTK_STATE_FLAG_SELECTED, },
936 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
937 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
938 { "focused", GTK_STATE_FLAG_FOCUSED, },
939 { "focus", GTK_STATE_FLAG_FOCUSED, },
940 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
944 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
945 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
946 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
947 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
949 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
951 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
953 if (pseudo_classes[i].state_flag)
954 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
956 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
958 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
960 encode_position (pseudo_classes[i].position_type,
961 pseudo_classes[i].position_a,
962 pseudo_classes[i].position_b));
967 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
969 _gtk_css_selector_free (selector);
973 static GtkCssSelector *
974 try_parse_name (GtkCssParser *parser,
975 GtkCssSelector *selector)
979 name = _gtk_css_parser_try_ident (parser, FALSE);
982 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
983 ? >K_CSS_SELECTOR_REGION
984 : >K_CSS_SELECTOR_NAME,
986 g_intern_string (name));
989 else if (_gtk_css_parser_try (parser, "*", FALSE))
990 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
995 static GtkCssSelector *
996 parse_simple_selector (GtkCssParser *parser,
997 GtkCssSelector *selector)
999 guint size = gtk_css_selector_size (selector);
1001 selector = try_parse_name (parser, selector);
1004 if (_gtk_css_parser_try (parser, "#", FALSE))
1005 selector = parse_selector_id (parser, selector);
1006 else if (_gtk_css_parser_try (parser, ".", FALSE))
1007 selector = parse_selector_class (parser, selector);
1008 else if (_gtk_css_parser_try (parser, ":", FALSE))
1009 selector = parse_selector_pseudo_class (parser, selector);
1010 else if (gtk_css_selector_size (selector) == size)
1012 _gtk_css_parser_error (parser, "Expected a valid selector");
1014 _gtk_css_selector_free (selector);
1020 while (selector && !_gtk_css_parser_is_eof (parser));
1022 _gtk_css_parser_skip_whitespace (parser);
1028 _gtk_css_selector_parse (GtkCssParser *parser)
1030 GtkCssSelector *selector = NULL;
1032 while ((selector = parse_simple_selector (parser, selector)) &&
1033 !_gtk_css_parser_is_eof (parser) &&
1034 !_gtk_css_parser_begins_with (parser, ',') &&
1035 !_gtk_css_parser_begins_with (parser, '{'))
1037 if (_gtk_css_parser_try (parser, "+", TRUE))
1038 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1039 else if (_gtk_css_parser_try (parser, "~", TRUE))
1040 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1041 else if (_gtk_css_parser_try (parser, ">", TRUE))
1042 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1044 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1051 _gtk_css_selector_free (GtkCssSelector *selector)
1053 g_return_if_fail (selector != NULL);
1059 _gtk_css_selector_print (const GtkCssSelector *selector,
1062 const GtkCssSelector *previous;
1064 g_return_if_fail (selector != NULL);
1066 previous = gtk_css_selector_previous (selector);
1068 _gtk_css_selector_print (previous, str);
1070 selector->class->print (selector, str);
1074 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1078 g_return_val_if_fail (selector != NULL, NULL);
1080 string = g_string_new (NULL);
1082 _gtk_css_selector_print (selector, string);
1084 return g_string_free (string, FALSE);
1088 _gtk_css_selector_get_change (const GtkCssSelector *selector)
1090 g_return_val_if_fail (selector != NULL, 0);
1092 return gtk_css_selector_get_change (selector);
1096 * _gtk_css_selector_matches:
1097 * @selector: the selector
1098 * @path: the path to check
1099 * @state: The state to match
1101 * Checks if the @selector matches the given @path. If @length is
1102 * smaller than the number of elements in @path, it is assumed that
1103 * only the first @length element of @path are valid and the rest
1104 * does not exist. This is useful for doing parent matches for the
1105 * 'inherit' keyword.
1107 * Returns: %TRUE if the selector matches @path
1110 _gtk_css_selector_matches (const GtkCssSelector *selector,
1111 const GtkCssMatcher *matcher)
1114 g_return_val_if_fail (selector != NULL, FALSE);
1115 g_return_val_if_fail (matcher != NULL, FALSE);
1117 return gtk_css_selector_match (selector, matcher);
1120 /* Computes specificity according to CSS 2.1.
1121 * The arguments must be initialized to 0 */
1123 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1128 for (; selector; selector = gtk_css_selector_previous (selector))
1130 const GtkCssSelectorClass *klass = selector->class;
1132 if (klass->increase_id_specificity)
1134 if (klass->increase_class_specificity)
1136 if (klass->increase_element_specificity)
1142 _gtk_css_selector_compare (const GtkCssSelector *a,
1143 const GtkCssSelector *b)
1145 guint a_ids = 0, a_classes = 0, a_elements = 0;
1146 guint b_ids = 0, b_classes = 0, b_elements = 0;
1149 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1150 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1152 compare = a_ids - b_ids;
1156 compare = a_classes - b_classes;
1160 return a_elements - b_elements;