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 *sibling;
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;
135 static const GtkCssSelectorTree *
136 gtk_css_selector_tree_get_parent (const GtkCssSelectorTree *tree)
141 static const GtkCssSelectorTree *
142 gtk_css_selector_tree_get_previous (const GtkCssSelectorTree *tree)
144 return tree->previous;
147 static const GtkCssSelectorTree *
148 gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
150 return tree->sibling;
156 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
159 g_string_append_c (string, ' ');
163 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
164 const GtkCssMatcher *matcher)
166 GtkCssMatcher ancestor;
168 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
172 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
180 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
181 const GtkCssMatcher *matcher,
184 GtkCssMatcher ancestor;
185 const GtkCssSelectorTree *prev;
187 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
191 for (prev = gtk_css_selector_tree_get_previous (tree);
193 prev = gtk_css_selector_tree_get_sibling (prev))
194 gtk_css_selector_tree_match (prev, matcher, res);
196 /* any matchers are dangerous here, as we may loop forever, but
197 we can terminate now as all possible matches have already been added */
198 if (_gtk_css_matcher_matches_any (matcher))
204 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
205 const GtkCssSelector *b)
211 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
213 return _gtk_css_change_for_child (previous_change);
216 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
218 gtk_css_selector_descendant_print,
219 gtk_css_selector_descendant_match,
220 gtk_css_selector_descendant_tree_match,
221 gtk_css_selector_descendant_get_change,
222 gtk_css_selector_descendant_compare_one,
223 FALSE, FALSE, FALSE, FALSE, FALSE
229 gtk_css_selector_child_print (const GtkCssSelector *selector,
232 g_string_append (string, " > ");
236 gtk_css_selector_child_match (const GtkCssSelector *selector,
237 const GtkCssMatcher *matcher)
239 GtkCssMatcher parent;
241 if (!_gtk_css_matcher_get_parent (&parent, matcher))
244 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
248 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
249 const GtkCssMatcher *matcher,
252 GtkCssMatcher parent;
253 const GtkCssSelectorTree *prev;
255 if (!_gtk_css_matcher_get_parent (&parent, matcher))
258 for (prev = gtk_css_selector_tree_get_previous (tree);
260 prev = gtk_css_selector_tree_get_sibling (prev))
261 gtk_css_selector_tree_match (prev, &parent, res);
265 gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
267 return _gtk_css_change_for_child (previous_change);
271 gtk_css_selector_child_compare_one (const GtkCssSelector *a,
272 const GtkCssSelector *b)
277 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
279 gtk_css_selector_child_print,
280 gtk_css_selector_child_match,
281 gtk_css_selector_child_tree_match,
282 gtk_css_selector_child_get_change,
283 gtk_css_selector_child_compare_one,
284 FALSE, FALSE, FALSE, FALSE, FALSE
290 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
293 g_string_append (string, " ~ ");
297 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
298 const GtkCssMatcher *matcher)
300 GtkCssMatcher previous;
302 while (_gtk_css_matcher_get_previous (&previous, matcher))
306 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
314 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
315 const GtkCssMatcher *matcher,
318 GtkCssMatcher previous;
319 const GtkCssSelectorTree *prev;
321 while (_gtk_css_matcher_get_previous (&previous, matcher))
325 for (prev = gtk_css_selector_tree_get_previous (tree);
327 prev = gtk_css_selector_tree_get_sibling (prev))
328 gtk_css_selector_tree_match (prev, matcher, res);
330 /* any matchers are dangerous here, as we may loop forever, but
331 we can terminate now as all possible matches have already been added */
332 if (_gtk_css_matcher_matches_any (matcher))
338 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
340 return _gtk_css_change_for_sibling (previous_change);
344 gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
345 const GtkCssSelector *b)
351 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
353 gtk_css_selector_sibling_print,
354 gtk_css_selector_sibling_match,
355 gtk_css_selector_sibling_tree_match,
356 gtk_css_selector_sibling_get_change,
357 gtk_css_selector_sibling_compare_one,
358 FALSE, FALSE, FALSE, FALSE, FALSE
364 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
367 g_string_append (string, " + ");
371 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
372 const GtkCssMatcher *matcher)
374 GtkCssMatcher previous;
376 if (!_gtk_css_matcher_get_previous (&previous, matcher))
379 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
383 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
384 const GtkCssMatcher *matcher,
387 GtkCssMatcher previous;
388 const GtkCssSelectorTree *prev;
390 if (!_gtk_css_matcher_get_previous (&previous, matcher))
395 for (prev = gtk_css_selector_tree_get_previous (tree);
397 prev = gtk_css_selector_tree_get_sibling (prev))
398 gtk_css_selector_tree_match (prev, matcher, res);
402 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
404 return _gtk_css_change_for_sibling (previous_change);
408 gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
409 const GtkCssSelector *b)
414 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
416 gtk_css_selector_adjacent_print,
417 gtk_css_selector_adjacent_match,
418 gtk_css_selector_adjacent_tree_match,
419 gtk_css_selector_adjacent_get_change,
420 gtk_css_selector_adjacent_compare_one,
421 FALSE, FALSE, FALSE, FALSE, FALSE
427 gtk_css_selector_any_print (const GtkCssSelector *selector,
430 g_string_append_c (string, '*');
434 gtk_css_selector_any_match (const GtkCssSelector *selector,
435 const GtkCssMatcher *matcher)
437 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
440 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
441 _gtk_css_matcher_has_regions (matcher))
443 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
447 return gtk_css_selector_match (previous, matcher);
451 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
452 const GtkCssMatcher *matcher,
455 const GtkCssSelectorTree *prev, *prev2;
457 gtk_css_selector_tree_found_match (tree, res);
459 for (prev = gtk_css_selector_tree_get_previous (tree);
461 prev = gtk_css_selector_tree_get_sibling (prev))
463 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
464 _gtk_css_matcher_has_regions (matcher))
466 for (prev2 = gtk_css_selector_tree_get_previous (prev);
468 prev2 = gtk_css_selector_tree_get_sibling (prev2))
469 gtk_css_selector_tree_match (prev2, matcher, res);
472 gtk_css_selector_tree_match (prev, matcher, res);
477 gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
479 return previous_change;
483 gtk_css_selector_any_compare_one (const GtkCssSelector *a,
484 const GtkCssSelector *b)
489 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
491 gtk_css_selector_any_print,
492 gtk_css_selector_any_match,
493 gtk_css_selector_any_tree_match,
494 gtk_css_selector_any_get_change,
495 gtk_css_selector_any_compare_one,
496 FALSE, FALSE, FALSE, TRUE, TRUE
502 gtk_css_selector_name_print (const GtkCssSelector *selector,
505 g_string_append (string, selector->data);
509 gtk_css_selector_name_match (const GtkCssSelector *selector,
510 const GtkCssMatcher *matcher)
512 if (!_gtk_css_matcher_has_name (matcher, selector->data))
515 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
519 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
520 const GtkCssMatcher *matcher,
523 const GtkCssSelectorTree *prev;
525 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
528 gtk_css_selector_tree_found_match (tree, res);
530 for (prev = gtk_css_selector_tree_get_previous (tree);
532 prev = gtk_css_selector_tree_get_sibling (prev))
533 gtk_css_selector_tree_match (prev, matcher, res);
538 gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
540 return previous_change | GTK_CSS_CHANGE_NAME;
544 gtk_css_selector_name_compare_one (const GtkCssSelector *a,
545 const GtkCssSelector *b)
547 return strcmp (a->data, b->data);
550 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
552 gtk_css_selector_name_print,
553 gtk_css_selector_name_match,
554 gtk_css_selector_name_tree_match,
555 gtk_css_selector_name_get_change,
556 gtk_css_selector_name_compare_one,
557 FALSE, FALSE, TRUE, TRUE, FALSE
563 gtk_css_selector_region_print (const GtkCssSelector *selector,
566 g_string_append (string, selector->data);
570 gtk_css_selector_region_match (const GtkCssSelector *selector,
571 const GtkCssMatcher *matcher)
573 const GtkCssSelector *previous;
575 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
578 previous = gtk_css_selector_previous (selector);
579 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
580 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
583 return gtk_css_selector_match (previous, matcher);
587 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
588 const GtkCssMatcher *matcher,
591 const GtkCssSelectorTree *prev, *prev2;
593 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
596 gtk_css_selector_tree_found_match (tree, res);
598 for (prev = gtk_css_selector_tree_get_previous (tree);
600 prev = gtk_css_selector_tree_get_sibling (prev))
602 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
604 for (prev2 = gtk_css_selector_tree_get_previous (prev);
606 prev2 = gtk_css_selector_tree_get_sibling (prev2))
607 gtk_css_selector_tree_match (prev2, matcher, res);
610 gtk_css_selector_tree_match (prev, matcher, res);
615 gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
619 change = previous_change;
620 change |= GTK_CSS_CHANGE_REGION;
621 change |= _gtk_css_change_for_child (change);
627 gtk_css_selector_region_compare_one (const GtkCssSelector *a,
628 const GtkCssSelector *b)
630 return strcmp (a->data, b->data);
633 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
635 gtk_css_selector_region_print,
636 gtk_css_selector_region_match,
637 gtk_css_selector_region_tree_match,
638 gtk_css_selector_region_get_change,
639 gtk_css_selector_region_compare_one,
640 FALSE, FALSE, TRUE, TRUE, TRUE
646 gtk_css_selector_class_print (const GtkCssSelector *selector,
649 g_string_append_c (string, '.');
650 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
654 gtk_css_selector_class_match (const GtkCssSelector *selector,
655 const GtkCssMatcher *matcher)
657 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
660 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
664 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
665 const GtkCssMatcher *matcher,
668 const GtkCssSelectorTree *prev;
670 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
673 gtk_css_selector_tree_found_match (tree, res);
675 for (prev = gtk_css_selector_tree_get_previous (tree);
677 prev = gtk_css_selector_tree_get_sibling (prev))
678 gtk_css_selector_tree_match (prev, matcher, res);
682 gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
684 return previous_change | GTK_CSS_CHANGE_CLASS;
688 gtk_css_selector_class_compare_one (const GtkCssSelector *a,
689 const GtkCssSelector *b)
691 return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
692 g_quark_to_string (GPOINTER_TO_UINT (b->data)));
695 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
697 gtk_css_selector_class_print,
698 gtk_css_selector_class_match,
699 gtk_css_selector_class_tree_match,
700 gtk_css_selector_class_get_change,
701 gtk_css_selector_class_compare_one,
702 FALSE, TRUE, FALSE, TRUE, FALSE
708 gtk_css_selector_id_print (const GtkCssSelector *selector,
711 g_string_append_c (string, '#');
712 g_string_append (string, selector->data);
716 gtk_css_selector_id_match (const GtkCssSelector *selector,
717 const GtkCssMatcher *matcher)
719 if (!_gtk_css_matcher_has_id (matcher, selector->data))
722 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
726 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
727 const GtkCssMatcher *matcher,
730 const GtkCssSelectorTree *prev;
732 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
735 gtk_css_selector_tree_found_match (tree, res);
737 for (prev = gtk_css_selector_tree_get_previous (tree);
739 prev = gtk_css_selector_tree_get_sibling (prev))
740 gtk_css_selector_tree_match (prev, matcher, res);
744 gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
746 return previous_change | GTK_CSS_CHANGE_ID;
751 gtk_css_selector_id_compare_one (const GtkCssSelector *a,
752 const GtkCssSelector *b)
754 return strcmp (a->data, b->data);
757 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
759 gtk_css_selector_id_print,
760 gtk_css_selector_id_match,
761 gtk_css_selector_id_tree_match,
762 gtk_css_selector_id_get_change,
763 gtk_css_selector_id_compare_one,
764 TRUE, FALSE, FALSE, TRUE, FALSE
767 /* PSEUDOCLASS FOR STATE */
770 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
773 static const char * state_names[] = {
784 state = GPOINTER_TO_UINT (selector->data);
785 g_string_append_c (string, ':');
787 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
789 if (state == (1 << i))
791 g_string_append (string, state_names[i]);
796 g_assert_not_reached ();
800 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
801 const GtkCssMatcher *matcher)
803 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
805 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
808 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
812 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
813 const GtkCssMatcher *matcher,
816 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
817 const GtkCssSelectorTree *prev;
819 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
822 gtk_css_selector_tree_found_match (tree, res);
824 for (prev = gtk_css_selector_tree_get_previous (tree);
826 prev = gtk_css_selector_tree_get_sibling (prev))
827 gtk_css_selector_tree_match (prev, matcher, res);
832 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
834 return previous_change | GTK_CSS_CHANGE_STATE;
838 gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
839 const GtkCssSelector *b)
841 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
844 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
846 gtk_css_selector_pseudoclass_state_print,
847 gtk_css_selector_pseudoclass_state_match,
848 gtk_css_selector_pseudoclass_state_tree_match,
849 gtk_css_selector_pseudoclass_state_get_change,
850 gtk_css_selector_pseudoclass_state_compare_one,
851 FALSE, TRUE, FALSE, TRUE, FALSE
854 /* PSEUDOCLASS FOR POSITION */
862 #define POSITION_TYPE_BITS 2
863 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
866 encode_position (PositionType type,
873 gssize type :POSITION_TYPE_BITS;
874 gssize a :POSITION_NUMBER_BITS;
875 gssize b :POSITION_NUMBER_BITS;
878 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
880 g_assert (type < (1 << POSITION_TYPE_BITS));
882 result.data.type = type;
890 decode_position (const GtkCssSelector *selector,
898 gssize type :POSITION_TYPE_BITS;
899 gssize a :POSITION_NUMBER_BITS;
900 gssize b :POSITION_NUMBER_BITS;
903 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
905 result.p = selector->data;
907 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
913 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
919 decode_position (selector, &type, &a, &b);
922 case POSITION_FORWARD:
926 g_string_append (string, ":first-child");
928 g_string_append_printf (string, ":nth-child(%d)", b);
930 else if (a == 2 && b == 0)
931 g_string_append (string, ":nth-child(even)");
932 else if (a == 2 && b == 1)
933 g_string_append (string, ":nth-child(odd)");
936 g_string_append (string, ":nth-child(");
938 g_string_append (string, "n");
940 g_string_append (string, "-n");
942 g_string_append_printf (string, "%dn", a);
944 g_string_append_printf (string, "+%d)", b);
946 g_string_append_printf (string, "%d)", b);
948 g_string_append (string, ")");
951 case POSITION_BACKWARD:
955 g_string_append (string, ":last-child");
957 g_string_append_printf (string, ":nth-last-child(%d)", b);
959 else if (a == 2 && b == 0)
960 g_string_append (string, ":nth-last-child(even)");
961 else if (a == 2 && b == 1)
962 g_string_append (string, ":nth-last-child(odd)");
965 g_string_append (string, ":nth-last-child(");
967 g_string_append (string, "n");
969 g_string_append (string, "-n");
971 g_string_append_printf (string, "%dn", a);
973 g_string_append_printf (string, "+%d)", b);
975 g_string_append_printf (string, "%d)", b);
977 g_string_append (string, ")");
981 g_string_append (string, ":only-child");
983 case POSITION_SORTED:
984 g_string_append (string, ":sorted");
987 g_assert_not_reached ();
993 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
994 const GtkCssMatcher *matcher)
996 GtkRegionFlags selector_flags;
997 const GtkCssSelector *previous;
1001 decode_position (selector, &type, &a, &b);
1004 case POSITION_FORWARD:
1005 if (a == 0 && b == 1)
1006 selector_flags = GTK_REGION_FIRST;
1007 else if (a == 2 && b == 0)
1008 selector_flags = GTK_REGION_EVEN;
1009 else if (a == 2 && b == 1)
1010 selector_flags = GTK_REGION_ODD;
1014 case POSITION_BACKWARD:
1015 if (a == 0 && b == 1)
1016 selector_flags = GTK_REGION_LAST;
1021 selector_flags = GTK_REGION_ONLY;
1023 case POSITION_SORTED:
1024 selector_flags = GTK_REGION_SORTED;
1027 g_assert_not_reached ();
1030 selector = gtk_css_selector_previous (selector);
1032 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
1035 previous = gtk_css_selector_previous (selector);
1036 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
1037 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
1040 return gtk_css_selector_match (previous, matcher);
1044 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
1045 const GtkCssMatcher *matcher)
1047 const GtkCssSelector *previous;
1051 previous = gtk_css_selector_previous (selector);
1052 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
1053 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
1055 decode_position (selector, &type, &a, &b);
1058 case POSITION_FORWARD:
1059 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1062 case POSITION_BACKWARD:
1063 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1067 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1068 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1071 case POSITION_SORTED:
1074 g_assert_not_reached ();
1078 return gtk_css_selector_match (previous, matcher);
1082 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
1083 const GtkCssSelectorTree *prev,
1084 const GtkCssMatcher *matcher,
1087 const GtkCssSelectorTree *prev2;
1088 GtkRegionFlags selector_flags;
1092 decode_position (&tree->selector, &type, &a, &b);
1095 case POSITION_FORWARD:
1096 if (a == 0 && b == 1)
1097 selector_flags = GTK_REGION_FIRST;
1098 else if (a == 2 && b == 0)
1099 selector_flags = GTK_REGION_EVEN;
1100 else if (a == 2 && b == 1)
1101 selector_flags = GTK_REGION_ODD;
1105 case POSITION_BACKWARD:
1106 if (a == 0 && b == 1)
1107 selector_flags = GTK_REGION_LAST;
1112 selector_flags = GTK_REGION_ONLY;
1114 case POSITION_SORTED:
1115 selector_flags = GTK_REGION_SORTED;
1118 g_assert_not_reached ();
1122 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1125 gtk_css_selector_tree_found_match (prev, res);
1127 for (prev2 = gtk_css_selector_tree_get_previous (prev);
1129 prev2 = gtk_css_selector_tree_get_sibling (prev2))
1131 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1132 gtk_css_selector_tree_match (gtk_css_selector_tree_get_previous (prev2), matcher, res);
1133 gtk_css_selector_tree_match (prev2, matcher, res);
1138 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1139 const GtkCssMatcher *matcher,
1142 const GtkCssSelectorTree *prev;
1146 for (prev = gtk_css_selector_tree_get_previous (tree);
1148 prev = gtk_css_selector_tree_get_sibling (prev))
1150 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1151 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1154 decode_position (&tree->selector, &type, &a, &b);
1157 case POSITION_FORWARD:
1158 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1161 case POSITION_BACKWARD:
1162 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1166 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1167 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1170 case POSITION_SORTED:
1173 g_assert_not_reached ();
1177 gtk_css_selector_tree_found_match (tree, res);
1179 for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
1181 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1182 gtk_css_selector_tree_match (prev, matcher, res);
1187 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1189 return previous_change | GTK_CSS_CHANGE_POSITION;
1193 gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
1194 const GtkCssSelector *b)
1196 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1199 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1200 "pseudoclass-position",
1201 gtk_css_selector_pseudoclass_position_print,
1202 gtk_css_selector_pseudoclass_position_match,
1203 gtk_css_selector_pseudoclass_position_tree_match,
1204 gtk_css_selector_pseudoclass_position_get_change,
1205 gtk_css_selector_pseudoclass_position_compare_one,
1206 FALSE, TRUE, FALSE, TRUE, TRUE
1212 gtk_css_selector_size (const GtkCssSelector *selector)
1218 selector = gtk_css_selector_previous (selector);
1225 static GtkCssSelector *
1226 gtk_css_selector_new (const GtkCssSelectorClass *class,
1227 GtkCssSelector *selector,
1232 size = gtk_css_selector_size (selector);
1233 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1235 selector[1].class = NULL;
1237 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1239 selector->class = class;
1240 selector->data = data;
1245 static GtkCssSelector *
1246 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1250 name = _gtk_css_parser_try_name (parser, FALSE);
1254 _gtk_css_parser_error (parser, "Expected a valid name for class");
1256 _gtk_css_selector_free (selector);
1260 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1262 GUINT_TO_POINTER (g_quark_from_string (name)));
1269 static GtkCssSelector *
1270 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1274 name = _gtk_css_parser_try_name (parser, FALSE);
1278 _gtk_css_parser_error (parser, "Expected a valid name for id");
1280 _gtk_css_selector_free (selector);
1284 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1286 g_intern_string (name));
1293 static GtkCssSelector *
1294 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1295 GtkCssSelector *selector,
1300 if (!_gtk_css_parser_try (parser, "(", TRUE))
1302 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1304 _gtk_css_selector_free (selector);
1308 if (_gtk_css_parser_try (parser, "even", TRUE))
1313 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1318 else if (type == POSITION_FORWARD &&
1319 _gtk_css_parser_try (parser, "first", TRUE))
1324 else if (type == POSITION_FORWARD &&
1325 _gtk_css_parser_try (parser, "last", TRUE))
1329 type = POSITION_BACKWARD;
1335 if (_gtk_css_parser_try (parser, "+", TRUE))
1337 else if (_gtk_css_parser_try (parser, "-", TRUE))
1342 if (_gtk_css_parser_try_int (parser, &a))
1346 _gtk_css_parser_error (parser, "Expected an integer");
1348 _gtk_css_selector_free (selector);
1353 else if (_gtk_css_parser_has_prefix (parser, "n"))
1359 _gtk_css_parser_error (parser, "Expected an integer");
1361 _gtk_css_selector_free (selector);
1365 if (_gtk_css_parser_try (parser, "n", TRUE))
1367 if (_gtk_css_parser_try (parser, "+", TRUE))
1369 else if (_gtk_css_parser_try (parser, "-", TRUE))
1374 if (_gtk_css_parser_try_int (parser, &b))
1378 _gtk_css_parser_error (parser, "Expected an integer");
1380 _gtk_css_selector_free (selector);
1396 if (!_gtk_css_parser_try (parser, ")", FALSE))
1398 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1400 _gtk_css_selector_free (selector);
1404 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1406 encode_position (type, a, b));
1411 static GtkCssSelector *
1412 parse_selector_pseudo_class (GtkCssParser *parser,
1413 GtkCssSelector *selector)
1415 static const struct {
1417 GtkStateFlags state_flag;
1418 PositionType position_type;
1421 } pseudo_classes[] = {
1422 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1423 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1424 { "only-child", 0, POSITION_ONLY, 0, 0 },
1425 { "sorted", 0, POSITION_SORTED, 0, 0 },
1426 { "active", GTK_STATE_FLAG_ACTIVE, },
1427 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1428 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1429 { "selected", GTK_STATE_FLAG_SELECTED, },
1430 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1431 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1432 { "focused", GTK_STATE_FLAG_FOCUSED, },
1433 { "focus", GTK_STATE_FLAG_FOCUSED, },
1434 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
1438 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1439 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1440 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1441 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1443 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1445 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1447 if (pseudo_classes[i].state_flag)
1448 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1450 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1452 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1454 encode_position (pseudo_classes[i].position_type,
1455 pseudo_classes[i].position_a,
1456 pseudo_classes[i].position_b));
1461 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1463 _gtk_css_selector_free (selector);
1467 static GtkCssSelector *
1468 try_parse_name (GtkCssParser *parser,
1469 GtkCssSelector *selector)
1473 name = _gtk_css_parser_try_ident (parser, FALSE);
1476 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
1477 ? >K_CSS_SELECTOR_REGION
1478 : >K_CSS_SELECTOR_NAME,
1480 g_intern_string (name));
1483 else if (_gtk_css_parser_try (parser, "*", FALSE))
1484 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1489 static GtkCssSelector *
1490 parse_simple_selector (GtkCssParser *parser,
1491 GtkCssSelector *selector)
1493 guint size = gtk_css_selector_size (selector);
1495 selector = try_parse_name (parser, selector);
1498 if (_gtk_css_parser_try (parser, "#", FALSE))
1499 selector = parse_selector_id (parser, selector);
1500 else if (_gtk_css_parser_try (parser, ".", FALSE))
1501 selector = parse_selector_class (parser, selector);
1502 else if (_gtk_css_parser_try (parser, ":", FALSE))
1503 selector = parse_selector_pseudo_class (parser, selector);
1504 else if (gtk_css_selector_size (selector) == size)
1506 _gtk_css_parser_error (parser, "Expected a valid selector");
1508 _gtk_css_selector_free (selector);
1514 while (selector && !_gtk_css_parser_is_eof (parser));
1516 _gtk_css_parser_skip_whitespace (parser);
1522 _gtk_css_selector_parse (GtkCssParser *parser)
1524 GtkCssSelector *selector = NULL;
1526 while ((selector = parse_simple_selector (parser, selector)) &&
1527 !_gtk_css_parser_is_eof (parser) &&
1528 !_gtk_css_parser_begins_with (parser, ',') &&
1529 !_gtk_css_parser_begins_with (parser, '{'))
1531 if (_gtk_css_parser_try (parser, "+", TRUE))
1532 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1533 else if (_gtk_css_parser_try (parser, "~", TRUE))
1534 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1535 else if (_gtk_css_parser_try (parser, ">", TRUE))
1536 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1538 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1545 _gtk_css_selector_free (GtkCssSelector *selector)
1547 g_return_if_fail (selector != NULL);
1553 _gtk_css_selector_print (const GtkCssSelector *selector,
1556 const GtkCssSelector *previous;
1558 g_return_if_fail (selector != NULL);
1560 previous = gtk_css_selector_previous (selector);
1562 _gtk_css_selector_print (previous, str);
1564 selector->class->print (selector, str);
1568 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1572 g_return_val_if_fail (selector != NULL, NULL);
1574 string = g_string_new (NULL);
1576 _gtk_css_selector_print (selector, string);
1578 return g_string_free (string, FALSE);
1583 _gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree)
1585 GtkCssChange change = 0;
1589 change = tree->selector.class->get_change (&tree->selector, change);
1590 tree = gtk_css_selector_tree_get_parent (tree);
1597 * _gtk_css_selector_matches:
1598 * @selector: the selector
1599 * @path: the path to check
1600 * @state: The state to match
1602 * Checks if the @selector matches the given @path. If @length is
1603 * smaller than the number of elements in @path, it is assumed that
1604 * only the first @length element of @path are valid and the rest
1605 * does not exist. This is useful for doing parent matches for the
1606 * 'inherit' keyword.
1608 * Returns: %TRUE if the selector matches @path
1611 _gtk_css_selector_matches (const GtkCssSelector *selector,
1612 const GtkCssMatcher *matcher)
1615 g_return_val_if_fail (selector != NULL, FALSE);
1616 g_return_val_if_fail (matcher != NULL, FALSE);
1618 return gtk_css_selector_match (selector, matcher);
1621 /* Computes specificity according to CSS 2.1.
1622 * The arguments must be initialized to 0 */
1624 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1629 for (; selector; selector = gtk_css_selector_previous (selector))
1631 const GtkCssSelectorClass *klass = selector->class;
1633 if (klass->increase_id_specificity)
1635 if (klass->increase_class_specificity)
1637 if (klass->increase_element_specificity)
1643 _gtk_css_selector_compare (const GtkCssSelector *a,
1644 const GtkCssSelector *b)
1646 guint a_ids = 0, a_classes = 0, a_elements = 0;
1647 guint b_ids = 0, b_classes = 0, b_elements = 0;
1650 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1651 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1653 compare = a_ids - b_ids;
1657 compare = a_classes - b_classes;
1661 return a_elements - b_elements;
1665 /******************** SelectorTree handling *****************/
1668 gtk_css_selectors_count_initial_init (void)
1670 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
1674 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
1676 if (!selector->class->is_simple || selector->class->must_keep_order)
1678 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1679 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1684 selector && selector->class->is_simple && !selector->class->must_keep_order;
1685 selector = gtk_css_selector_previous (selector))
1687 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1688 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1693 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
1695 if (!selector->class->is_simple || selector->class->must_keep_order)
1696 return gtk_css_selector_equal (selector, initial);
1699 selector && selector->class->is_simple && !selector->class->must_keep_order;
1700 selector = gtk_css_selector_previous (selector))
1702 if (gtk_css_selector_equal (selector, initial))
1709 static GtkCssSelector *
1710 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
1712 GtkCssSelector *found;
1715 /* If the initial simple selector is not first, move it there so we can skip it
1716 without losing any other selectors */
1717 if (!gtk_css_selector_equal (selector, initial))
1719 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
1721 if (gtk_css_selector_equal (found, initial))
1725 g_assert (found != NULL && found->class->is_simple);
1732 return (GtkCssSelector *)gtk_css_selector_previous (selector);
1736 direct_ptr_compare (const void *_a, const void *_b)
1738 gpointer *a = (gpointer *)_a;
1739 gpointer *b = (gpointer *)_b;
1748 _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
1749 const GtkCssMatcher *matcher)
1753 GHashTableIter iter;
1756 res = g_hash_table_new (g_direct_hash, g_direct_equal);
1758 for (; tree != NULL;
1759 tree = gtk_css_selector_tree_get_sibling (tree))
1760 gtk_css_selector_tree_match (tree, matcher, res);
1762 array = g_ptr_array_sized_new (g_hash_table_size (res));
1764 g_hash_table_iter_init (&iter, res);
1765 while (g_hash_table_iter_next (&iter, &key, NULL))
1766 g_ptr_array_add (array, key);
1768 g_hash_table_destroy (res);
1770 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
1777 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
1779 gboolean first = TRUE;
1782 for (; tree != NULL; tree = tree->siblings, first = FALSE)
1785 g_string_append (str, prefix);
1790 g_string_append (str, "─┬─");
1792 g_string_append (str, "───");
1797 g_string_append (str, " ├─");
1799 g_string_append (str, " └─");
1803 tree->selector.class->print (&tree->selector, str);
1804 len = str->len - len;
1806 if (gtk_css_selector_tree_get_previous (tree))
1808 GString *prefix2 = g_string_new (prefix);
1811 g_string_append (prefix2, " │ ");
1813 g_string_append (prefix2, " ");
1814 for (i = 0; i < len; i++)
1815 g_string_append_c (prefix2, ' ');
1817 _gtk_css_selector_tree_print (gtk_css_selector_tree_get_previous (tree), str, prefix2->str);
1818 g_string_free (prefix2, TRUE);
1821 g_string_append (str, "\n");
1827 _gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
1830 const GtkCssSelectorTree *parent;
1832 g_return_if_fail (tree != NULL);
1834 tree->selector.class->print (&tree->selector, str);
1836 parent = gtk_css_selector_tree_get_parent (tree);
1838 _gtk_css_selector_tree_match_print (parent, str);
1842 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
1847 _gtk_css_selector_tree_free (tree->sibling);
1848 _gtk_css_selector_tree_free (tree->previous);
1856 GtkCssSelector *current_selector;
1857 GtkCssSelectorTree **selector_match;
1858 } GtkCssSelectorRuleSetInfo;
1861 static GtkCssSelectorTree *
1862 subdivide_infos (GList *infos, GtkCssSelectorTree *parent)
1864 GHashTable *ht = gtk_css_selectors_count_initial_init ();
1868 GtkCssSelectorTree *tree;
1869 GtkCssSelectorRuleSetInfo *info;
1870 GtkCssSelector *max_selector;
1871 GHashTableIter iter;
1873 gpointer key, value;
1874 GPtrArray *exact_matches;
1879 for (l = infos; l != NULL; l = l->next)
1882 gtk_css_selectors_count_initial (info->current_selector, ht);
1885 /* Pick the selector with highest count, and use as decision on this level
1886 as that makes it possible to skip the largest amount of checks later */
1889 max_selector = NULL;
1891 g_hash_table_iter_init (&iter, ht);
1892 while (g_hash_table_iter_next (&iter, &key, &value))
1894 GtkCssSelector *selector = key;
1895 if (GPOINTER_TO_UINT (value) > max_count ||
1896 (GPOINTER_TO_UINT (value) == max_count &&
1897 gtk_css_selector_compare_one (selector, max_selector) < 0))
1899 max_count = GPOINTER_TO_UINT (value);
1900 max_selector = selector;
1907 tree = g_new0 (GtkCssSelectorTree, 1);
1908 tree->parent = parent;
1909 tree->selector = *max_selector;
1911 exact_matches = NULL;
1912 for (l = infos; l != NULL; l = l->next)
1916 if (gtk_css_selectors_has_initial_selector (info->current_selector, &tree->selector))
1918 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &tree->selector);
1919 if (info->current_selector == NULL)
1921 /* Matches current node */
1922 if (exact_matches == NULL)
1923 exact_matches = g_ptr_array_new ();
1924 g_ptr_array_add (exact_matches, info->match);
1925 if (info->selector_match != NULL)
1926 *info->selector_match = tree;
1929 matched = g_list_prepend (matched, info);
1933 remaining = g_list_prepend (remaining, info);
1939 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
1940 tree->matches = g_ptr_array_free (exact_matches, FALSE);
1944 tree->previous = subdivide_infos (matched, tree);
1947 tree->sibling = subdivide_infos (remaining, parent);
1952 struct _GtkCssSelectorTreeBuilder {
1956 GtkCssSelectorTreeBuilder *
1957 _gtk_css_selector_tree_builder_new (void)
1959 return g_new0 (GtkCssSelectorTreeBuilder, 1);
1963 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
1965 g_list_free_full (builder->infos, g_free);
1970 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
1971 GtkCssSelector *selectors,
1972 GtkCssSelectorTree **selector_match,
1975 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
1977 info->match = match;
1978 info->current_selector = selectors;
1979 info->selector_match = selector_match;
1980 builder->infos = g_list_prepend (builder->infos, info);
1983 GtkCssSelectorTree *
1984 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
1986 GtkCssSelectorTree *tree;
1988 tree = subdivide_infos (builder->infos, NULL);
1992 GString *s = g_string_new ("");
1993 _gtk_css_selector_tree_print (tree, s, "");
1994 g_print ("%s", s->str);
1995 g_string_free (s, TRUE);