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;
43 struct _GtkCssSelector
45 const GtkCssSelectorClass *class; /* type of check this selector does */
46 gconstpointer data; /* data for matching:
47 - interned string for CLASS, NAME and ID
48 - GUINT_TO_POINTER() for PSEUDOCLASS_REGION/STATE */
52 gtk_css_selector_match (const GtkCssSelector *selector,
53 const GtkCssMatcher *matcher)
58 return selector->class->match (selector, matcher);
62 gtk_css_selector_get_change (const GtkCssSelector *selector)
67 return selector->class->get_change (selector);
70 static const GtkCssSelector *
71 gtk_css_selector_previous (const GtkCssSelector *selector)
73 selector = selector + 1;
75 return selector->class ? selector : NULL;
81 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
84 g_string_append_c (string, ' ');
88 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
89 const GtkCssMatcher *matcher)
91 GtkCssMatcher ancestor;
93 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
97 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
105 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector)
107 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
110 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
112 gtk_css_selector_descendant_print,
113 gtk_css_selector_descendant_match,
114 gtk_css_selector_descendant_get_change,
121 gtk_css_selector_child_print (const GtkCssSelector *selector,
124 g_string_append (string, " > ");
128 gtk_css_selector_child_match (const GtkCssSelector *selector,
129 const GtkCssMatcher *matcher)
131 GtkCssMatcher parent;
133 if (!_gtk_css_matcher_get_parent (&parent, matcher))
136 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
140 gtk_css_selector_child_get_change (const GtkCssSelector *selector)
142 return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
145 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
147 gtk_css_selector_child_print,
148 gtk_css_selector_child_match,
149 gtk_css_selector_child_get_change,
156 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
159 g_string_append (string, " ~ ");
163 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
164 const GtkCssMatcher *matcher)
166 GtkCssMatcher previous;
168 while (_gtk_css_matcher_get_previous (&previous, matcher))
172 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
180 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector)
182 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
185 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
187 gtk_css_selector_sibling_print,
188 gtk_css_selector_sibling_match,
189 gtk_css_selector_sibling_get_change,
196 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
199 g_string_append (string, " + ");
203 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
204 const GtkCssMatcher *matcher)
206 GtkCssMatcher previous;
208 if (!_gtk_css_matcher_get_previous (&previous, matcher))
211 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
215 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector)
217 return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
220 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
222 gtk_css_selector_adjacent_print,
223 gtk_css_selector_adjacent_match,
224 gtk_css_selector_adjacent_get_change,
231 gtk_css_selector_any_print (const GtkCssSelector *selector,
234 g_string_append_c (string, '*');
238 gtk_css_selector_any_match (const GtkCssSelector *selector,
239 const GtkCssMatcher *matcher)
241 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
244 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
245 _gtk_css_matcher_has_regions (matcher))
247 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
251 return gtk_css_selector_match (previous, matcher);
255 gtk_css_selector_any_get_change (const GtkCssSelector *selector)
257 return gtk_css_selector_get_change (gtk_css_selector_previous (selector));
260 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
262 gtk_css_selector_any_print,
263 gtk_css_selector_any_match,
264 gtk_css_selector_any_get_change,
271 gtk_css_selector_name_print (const GtkCssSelector *selector,
274 g_string_append (string, selector->data);
278 gtk_css_selector_name_match (const GtkCssSelector *selector,
279 const GtkCssMatcher *matcher)
281 if (!_gtk_css_matcher_has_name (matcher, selector->data))
284 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
288 gtk_css_selector_name_get_change (const GtkCssSelector *selector)
290 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_NAME;
293 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
295 gtk_css_selector_name_print,
296 gtk_css_selector_name_match,
297 gtk_css_selector_name_get_change,
304 gtk_css_selector_region_print (const GtkCssSelector *selector,
307 g_string_append (string, selector->data);
311 gtk_css_selector_region_match (const GtkCssSelector *selector,
312 const GtkCssMatcher *matcher)
314 const GtkCssSelector *previous;
316 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
319 previous = gtk_css_selector_previous (selector);
320 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
321 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
324 return gtk_css_selector_match (previous, matcher);
328 gtk_css_selector_region_get_change (const GtkCssSelector *selector)
332 change = gtk_css_selector_get_change (gtk_css_selector_previous (selector));
333 change |= GTK_CSS_CHANGE_REGION;
334 change |= _gtk_css_change_for_child (change);
339 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
341 gtk_css_selector_region_print,
342 gtk_css_selector_region_match,
343 gtk_css_selector_region_get_change,
350 gtk_css_selector_class_print (const GtkCssSelector *selector,
353 g_string_append_c (string, '.');
354 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
358 gtk_css_selector_class_match (const GtkCssSelector *selector,
359 const GtkCssMatcher *matcher)
361 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
364 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
368 gtk_css_selector_class_get_change (const GtkCssSelector *selector)
370 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_CLASS;
373 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
375 gtk_css_selector_class_print,
376 gtk_css_selector_class_match,
377 gtk_css_selector_class_get_change,
384 gtk_css_selector_id_print (const GtkCssSelector *selector,
387 g_string_append_c (string, '#');
388 g_string_append (string, selector->data);
392 gtk_css_selector_id_match (const GtkCssSelector *selector,
393 const GtkCssMatcher *matcher)
395 if (!_gtk_css_matcher_has_id (matcher, selector->data))
398 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
402 gtk_css_selector_id_get_change (const GtkCssSelector *selector)
404 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_ID;
407 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
409 gtk_css_selector_id_print,
410 gtk_css_selector_id_match,
411 gtk_css_selector_id_get_change,
415 /* PSEUDOCLASS FOR STATE */
418 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
421 static const char * state_names[] = {
432 state = GPOINTER_TO_UINT (selector->data);
433 g_string_append_c (string, ':');
435 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
437 if (state == (1 << i))
439 g_string_append (string, state_names[i]);
444 g_assert_not_reached ();
448 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
449 const GtkCssMatcher *matcher)
451 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
453 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
456 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
460 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector)
462 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_STATE;
465 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
467 gtk_css_selector_pseudoclass_state_print,
468 gtk_css_selector_pseudoclass_state_match,
469 gtk_css_selector_pseudoclass_state_get_change,
473 /* PSEUDOCLASS FOR POSITION */
481 #define POSITION_TYPE_BITS 2
482 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
485 encode_position (PositionType type,
492 gssize type :POSITION_TYPE_BITS;
493 gssize a :POSITION_NUMBER_BITS;
494 gssize b :POSITION_NUMBER_BITS;
497 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
499 g_assert (type < (1 << POSITION_TYPE_BITS));
501 result.data.type = type;
509 decode_position (const GtkCssSelector *selector,
517 gssize type :POSITION_TYPE_BITS;
518 gssize a :POSITION_NUMBER_BITS;
519 gssize b :POSITION_NUMBER_BITS;
522 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
524 result.p = selector->data;
526 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
532 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
538 decode_position (selector, &type, &a, &b);
541 case POSITION_FORWARD:
545 g_string_append (string, ":first-child");
547 g_string_append_printf (string, ":nth-child(%d)", b);
549 else if (a == 2 && b == 0)
550 g_string_append (string, ":nth-child(even)");
551 else if (a == 2 && b == 1)
552 g_string_append (string, ":nth-child(odd)");
555 g_string_append (string, ":nth-child(");
557 g_string_append (string, "n");
559 g_string_append (string, "-n");
561 g_string_append_printf (string, "%dn", a);
563 g_string_append_printf (string, "+%d)", b);
565 g_string_append_printf (string, "%d)", b);
567 g_string_append (string, ")");
570 case POSITION_BACKWARD:
574 g_string_append (string, ":last-child");
576 g_string_append_printf (string, ":nth-last-child(%d)", b);
578 else if (a == 2 && b == 0)
579 g_string_append (string, ":nth-last-child(even)");
580 else if (a == 2 && b == 1)
581 g_string_append (string, ":nth-last-child(odd)");
584 g_string_append (string, ":nth-last-child(");
586 g_string_append (string, "n");
588 g_string_append (string, "-n");
590 g_string_append_printf (string, "%dn", a);
592 g_string_append_printf (string, "+%d)", b);
594 g_string_append_printf (string, "%d)", b);
596 g_string_append (string, ")");
600 g_string_append (string, ":only-child");
602 case POSITION_SORTED:
603 g_string_append (string, ":sorted");
606 g_assert_not_reached ();
612 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
613 const GtkCssMatcher *matcher)
615 GtkRegionFlags selector_flags;
616 const GtkCssSelector *previous;
620 decode_position (selector, &type, &a, &b);
623 case POSITION_FORWARD:
624 if (a == 0 && b == 1)
625 selector_flags = GTK_REGION_FIRST;
626 else if (a == 2 && b == 0)
627 selector_flags = GTK_REGION_EVEN;
628 else if (a == 2 && b == 1)
629 selector_flags = GTK_REGION_ODD;
633 case POSITION_BACKWARD:
634 if (a == 0 && b == 1)
635 selector_flags = GTK_REGION_LAST;
640 selector_flags = GTK_REGION_ONLY;
642 case POSITION_SORTED:
643 selector_flags = GTK_REGION_SORTED;
646 g_assert_not_reached ();
649 selector = gtk_css_selector_previous (selector);
651 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
654 previous = gtk_css_selector_previous (selector);
655 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
656 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
659 return gtk_css_selector_match (previous, matcher);
663 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
664 const GtkCssMatcher *matcher)
666 const GtkCssSelector *previous;
670 previous = gtk_css_selector_previous (selector);
671 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
672 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
674 decode_position (selector, &type, &a, &b);
677 case POSITION_FORWARD:
678 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
681 case POSITION_BACKWARD:
682 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
686 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
687 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
690 case POSITION_SORTED:
693 g_assert_not_reached ();
697 return gtk_css_selector_match (previous, matcher);
701 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector)
703 return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_POSITION;
706 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
707 "pseudoclass-position",
708 gtk_css_selector_pseudoclass_position_print,
709 gtk_css_selector_pseudoclass_position_match,
710 gtk_css_selector_pseudoclass_position_get_change,
717 gtk_css_selector_size (const GtkCssSelector *selector)
723 selector = gtk_css_selector_previous (selector);
730 static GtkCssSelector *
731 gtk_css_selector_new (const GtkCssSelectorClass *class,
732 GtkCssSelector *selector,
737 size = gtk_css_selector_size (selector);
738 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
740 selector[1].class = NULL;
742 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
744 selector->class = class;
745 selector->data = data;
750 static GtkCssSelector *
751 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
755 name = _gtk_css_parser_try_name (parser, FALSE);
759 _gtk_css_parser_error (parser, "Expected a valid name for class");
761 _gtk_css_selector_free (selector);
765 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
767 GUINT_TO_POINTER (g_quark_from_string (name)));
774 static GtkCssSelector *
775 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
779 name = _gtk_css_parser_try_name (parser, FALSE);
783 _gtk_css_parser_error (parser, "Expected a valid name for id");
785 _gtk_css_selector_free (selector);
789 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
791 g_intern_string (name));
798 static GtkCssSelector *
799 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
800 GtkCssSelector *selector,
805 if (!_gtk_css_parser_try (parser, "(", TRUE))
807 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
809 _gtk_css_selector_free (selector);
813 if (_gtk_css_parser_try (parser, "even", TRUE))
818 else if (_gtk_css_parser_try (parser, "odd", TRUE))
823 else if (type == POSITION_FORWARD &&
824 _gtk_css_parser_try (parser, "first", TRUE))
829 else if (type == POSITION_FORWARD &&
830 _gtk_css_parser_try (parser, "last", TRUE))
834 type = POSITION_BACKWARD;
840 if (_gtk_css_parser_try (parser, "+", TRUE))
842 else if (_gtk_css_parser_try (parser, "-", TRUE))
847 if (_gtk_css_parser_try_int (parser, &a))
851 _gtk_css_parser_error (parser, "Expected an integer");
853 _gtk_css_selector_free (selector);
858 else if (_gtk_css_parser_has_prefix (parser, "n"))
864 _gtk_css_parser_error (parser, "Expected an integer");
866 _gtk_css_selector_free (selector);
870 if (_gtk_css_parser_try (parser, "n", TRUE))
872 if (_gtk_css_parser_try (parser, "+", TRUE))
874 else if (_gtk_css_parser_try (parser, "-", TRUE))
879 if (_gtk_css_parser_try_int (parser, &b))
883 _gtk_css_parser_error (parser, "Expected an integer");
885 _gtk_css_selector_free (selector);
901 if (!_gtk_css_parser_try (parser, ")", FALSE))
903 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
905 _gtk_css_selector_free (selector);
909 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
911 encode_position (type, a, b));
916 static GtkCssSelector *
917 parse_selector_pseudo_class (GtkCssParser *parser,
918 GtkCssSelector *selector)
920 static const struct {
922 GtkStateFlags state_flag;
923 PositionType position_type;
926 } pseudo_classes[] = {
927 { "first-child", 0, POSITION_FORWARD, 0, 1 },
928 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
929 { "only-child", 0, POSITION_ONLY, 0, 0 },
930 { "sorted", 0, POSITION_SORTED, 0, 0 },
931 { "active", GTK_STATE_FLAG_ACTIVE, },
932 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
933 { "hover", GTK_STATE_FLAG_PRELIGHT, },
934 { "selected", GTK_STATE_FLAG_SELECTED, },
935 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
936 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
937 { "focused", GTK_STATE_FLAG_FOCUSED, },
938 { "focus", GTK_STATE_FLAG_FOCUSED, },
939 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
943 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
944 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
945 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
946 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
948 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
950 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
952 if (pseudo_classes[i].state_flag)
953 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
955 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
957 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
959 encode_position (pseudo_classes[i].position_type,
960 pseudo_classes[i].position_a,
961 pseudo_classes[i].position_b));
966 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
968 _gtk_css_selector_free (selector);
972 static GtkCssSelector *
973 try_parse_name (GtkCssParser *parser,
974 GtkCssSelector *selector)
978 name = _gtk_css_parser_try_ident (parser, FALSE);
981 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
982 ? >K_CSS_SELECTOR_REGION
983 : >K_CSS_SELECTOR_NAME,
985 g_intern_string (name));
988 else if (_gtk_css_parser_try (parser, "*", FALSE))
989 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
994 static GtkCssSelector *
995 parse_simple_selector (GtkCssParser *parser,
996 GtkCssSelector *selector)
998 guint size = gtk_css_selector_size (selector);
1000 selector = try_parse_name (parser, selector);
1003 if (_gtk_css_parser_try (parser, "#", FALSE))
1004 selector = parse_selector_id (parser, selector);
1005 else if (_gtk_css_parser_try (parser, ".", FALSE))
1006 selector = parse_selector_class (parser, selector);
1007 else if (_gtk_css_parser_try (parser, ":", FALSE))
1008 selector = parse_selector_pseudo_class (parser, selector);
1009 else if (gtk_css_selector_size (selector) == size)
1011 _gtk_css_parser_error (parser, "Expected a valid selector");
1013 _gtk_css_selector_free (selector);
1019 while (selector && !_gtk_css_parser_is_eof (parser));
1021 _gtk_css_parser_skip_whitespace (parser);
1027 _gtk_css_selector_parse (GtkCssParser *parser)
1029 GtkCssSelector *selector = NULL;
1031 while ((selector = parse_simple_selector (parser, selector)) &&
1032 !_gtk_css_parser_is_eof (parser) &&
1033 !_gtk_css_parser_begins_with (parser, ',') &&
1034 !_gtk_css_parser_begins_with (parser, '{'))
1036 if (_gtk_css_parser_try (parser, "+", TRUE))
1037 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1038 else if (_gtk_css_parser_try (parser, "~", TRUE))
1039 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1040 else if (_gtk_css_parser_try (parser, ">", TRUE))
1041 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1043 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1050 _gtk_css_selector_free (GtkCssSelector *selector)
1052 g_return_if_fail (selector != NULL);
1058 _gtk_css_selector_print (const GtkCssSelector *selector,
1061 const GtkCssSelector *previous;
1063 g_return_if_fail (selector != NULL);
1065 previous = gtk_css_selector_previous (selector);
1067 _gtk_css_selector_print (previous, str);
1069 selector->class->print (selector, str);
1073 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1077 g_return_val_if_fail (selector != NULL, NULL);
1079 string = g_string_new (NULL);
1081 _gtk_css_selector_print (selector, string);
1083 return g_string_free (string, FALSE);
1087 _gtk_css_selector_get_change (const GtkCssSelector *selector)
1089 g_return_val_if_fail (selector != NULL, 0);
1091 return gtk_css_selector_get_change (selector);
1095 * _gtk_css_selector_matches:
1096 * @selector: the selector
1097 * @path: the path to check
1098 * @state: The state to match
1100 * Checks if the @selector matches the given @path. If @length is
1101 * smaller than the number of elements in @path, it is assumed that
1102 * only the first @length element of @path are valid and the rest
1103 * does not exist. This is useful for doing parent matches for the
1104 * 'inherit' keyword.
1106 * Returns: %TRUE if the selector matches @path
1109 _gtk_css_selector_matches (const GtkCssSelector *selector,
1110 const GtkCssMatcher *matcher)
1113 g_return_val_if_fail (selector != NULL, FALSE);
1114 g_return_val_if_fail (matcher != NULL, FALSE);
1116 return gtk_css_selector_match (selector, matcher);
1119 /* Computes specificity according to CSS 2.1.
1120 * The arguments must be initialized to 0 */
1122 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1127 for (; selector; selector = gtk_css_selector_previous (selector))
1129 const GtkCssSelectorClass *klass = selector->class;
1131 if (klass->increase_id_specificity)
1133 if (klass->increase_class_specificity)
1135 if (klass->increase_element_specificity)
1141 _gtk_css_selector_compare (const GtkCssSelector *a,
1142 const GtkCssSelector *b)
1144 guint a_ids = 0, a_classes = 0, a_elements = 0;
1145 guint b_ids = 0, b_classes = 0, b_elements = 0;
1148 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1149 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1151 compare = a_ids - b_ids;
1155 compare = a_classes - b_classes;
1159 return a_elements - b_elements;
1163 _gtk_css_selector_get_state_flags (const GtkCssSelector *selector)
1165 GtkStateFlags state = 0;
1167 g_return_val_if_fail (selector != NULL, 0);
1169 for (; selector && selector->class == >K_CSS_SELECTOR_NAME; selector = gtk_css_selector_previous (selector))
1170 state |= GPOINTER_TO_UINT (selector->data);