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 #define GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET G_MAXINT32
61 struct _GtkCssSelectorTree
63 GtkCssSelector selector;
65 gint32 previous_offset;
66 gint32 sibling_offset;
67 gint32 matches_offset; /* pointers that we return as matches if selector matches */
71 gtk_css_selector_equal (const GtkCssSelector *a,
72 const GtkCssSelector *b)
75 a->class == b->class &&
80 gtk_css_selector_hash (const GtkCssSelector *selector)
82 return GPOINTER_TO_UINT (selector->class) ^ GPOINTER_TO_UINT (selector->data);
86 gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
88 if (tree->matches_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
91 return (gpointer *) ((guint8 *)tree + tree->matches_offset);
95 gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
101 matches = gtk_css_selector_tree_get_matches (tree);
104 for (i = 0; matches[i] != NULL; i++)
105 g_hash_table_insert (res, matches[i], matches[i]);
110 gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
111 const GtkCssMatcher *matcher,
117 tree->selector.class->tree_match (tree, matcher, res);
121 gtk_css_selector_match (const GtkCssSelector *selector,
122 const GtkCssMatcher *matcher)
124 if (selector == NULL)
127 return selector->class->match (selector, matcher);
131 gtk_css_selector_compare_one (const GtkCssSelector *a, const GtkCssSelector *b)
133 if (a->class != b->class)
134 return strcmp (a->class->name, b->class->name);
136 return a->class->compare_one (a, b);
139 static const GtkCssSelector *
140 gtk_css_selector_previous (const GtkCssSelector *selector)
142 selector = selector + 1;
144 return selector->class ? selector : NULL;
147 static const GtkCssSelectorTree *
148 gtk_css_selector_tree_at_offset (const GtkCssSelectorTree *tree,
151 if (offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
154 return (GtkCssSelectorTree *) ((guint8 *)tree + offset);
157 static const GtkCssSelectorTree *
158 gtk_css_selector_tree_get_parent (const GtkCssSelectorTree *tree)
160 return gtk_css_selector_tree_at_offset (tree, tree->parent_offset);
163 static const GtkCssSelectorTree *
164 gtk_css_selector_tree_get_previous (const GtkCssSelectorTree *tree)
166 return gtk_css_selector_tree_at_offset (tree, tree->previous_offset);
169 static const GtkCssSelectorTree *
170 gtk_css_selector_tree_get_sibling (const GtkCssSelectorTree *tree)
172 return gtk_css_selector_tree_at_offset (tree, tree->sibling_offset);
178 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
181 g_string_append_c (string, ' ');
185 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
186 const GtkCssMatcher *matcher)
188 GtkCssMatcher ancestor;
190 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
194 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
202 gtk_css_selector_descendant_tree_match (const GtkCssSelectorTree *tree,
203 const GtkCssMatcher *matcher,
206 GtkCssMatcher ancestor;
207 const GtkCssSelectorTree *prev;
209 while (_gtk_css_matcher_get_parent (&ancestor, matcher))
213 for (prev = gtk_css_selector_tree_get_previous (tree);
215 prev = gtk_css_selector_tree_get_sibling (prev))
216 gtk_css_selector_tree_match (prev, matcher, res);
218 /* any matchers are dangerous here, as we may loop forever, but
219 we can terminate now as all possible matches have already been added */
220 if (_gtk_css_matcher_matches_any (matcher))
226 gtk_css_selector_descendant_compare_one (const GtkCssSelector *a,
227 const GtkCssSelector *b)
233 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
235 return _gtk_css_change_for_child (previous_change);
238 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
240 gtk_css_selector_descendant_print,
241 gtk_css_selector_descendant_match,
242 gtk_css_selector_descendant_tree_match,
243 gtk_css_selector_descendant_get_change,
244 gtk_css_selector_descendant_compare_one,
245 FALSE, FALSE, FALSE, FALSE, FALSE
251 gtk_css_selector_child_print (const GtkCssSelector *selector,
254 g_string_append (string, " > ");
258 gtk_css_selector_child_match (const GtkCssSelector *selector,
259 const GtkCssMatcher *matcher)
261 GtkCssMatcher parent;
263 if (!_gtk_css_matcher_get_parent (&parent, matcher))
266 return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
270 gtk_css_selector_child_tree_match (const GtkCssSelectorTree *tree,
271 const GtkCssMatcher *matcher,
274 GtkCssMatcher parent;
275 const GtkCssSelectorTree *prev;
277 if (!_gtk_css_matcher_get_parent (&parent, matcher))
280 for (prev = gtk_css_selector_tree_get_previous (tree);
282 prev = gtk_css_selector_tree_get_sibling (prev))
283 gtk_css_selector_tree_match (prev, &parent, res);
287 gtk_css_selector_child_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
289 return _gtk_css_change_for_child (previous_change);
293 gtk_css_selector_child_compare_one (const GtkCssSelector *a,
294 const GtkCssSelector *b)
299 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
301 gtk_css_selector_child_print,
302 gtk_css_selector_child_match,
303 gtk_css_selector_child_tree_match,
304 gtk_css_selector_child_get_change,
305 gtk_css_selector_child_compare_one,
306 FALSE, FALSE, FALSE, FALSE, FALSE
312 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
315 g_string_append (string, " ~ ");
319 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
320 const GtkCssMatcher *matcher)
322 GtkCssMatcher previous;
324 while (_gtk_css_matcher_get_previous (&previous, matcher))
328 if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
336 gtk_css_selector_sibling_tree_match (const GtkCssSelectorTree *tree,
337 const GtkCssMatcher *matcher,
340 GtkCssMatcher previous;
341 const GtkCssSelectorTree *prev;
343 while (_gtk_css_matcher_get_previous (&previous, matcher))
347 for (prev = gtk_css_selector_tree_get_previous (tree);
349 prev = gtk_css_selector_tree_get_sibling (prev))
350 gtk_css_selector_tree_match (prev, matcher, res);
352 /* any matchers are dangerous here, as we may loop forever, but
353 we can terminate now as all possible matches have already been added */
354 if (_gtk_css_matcher_matches_any (matcher))
360 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
362 return _gtk_css_change_for_sibling (previous_change);
366 gtk_css_selector_sibling_compare_one (const GtkCssSelector *a,
367 const GtkCssSelector *b)
373 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
375 gtk_css_selector_sibling_print,
376 gtk_css_selector_sibling_match,
377 gtk_css_selector_sibling_tree_match,
378 gtk_css_selector_sibling_get_change,
379 gtk_css_selector_sibling_compare_one,
380 FALSE, FALSE, FALSE, FALSE, FALSE
386 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
389 g_string_append (string, " + ");
393 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
394 const GtkCssMatcher *matcher)
396 GtkCssMatcher previous;
398 if (!_gtk_css_matcher_get_previous (&previous, matcher))
401 return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
405 gtk_css_selector_adjacent_tree_match (const GtkCssSelectorTree *tree,
406 const GtkCssMatcher *matcher,
409 GtkCssMatcher previous;
410 const GtkCssSelectorTree *prev;
412 if (!_gtk_css_matcher_get_previous (&previous, matcher))
417 for (prev = gtk_css_selector_tree_get_previous (tree);
419 prev = gtk_css_selector_tree_get_sibling (prev))
420 gtk_css_selector_tree_match (prev, matcher, res);
424 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
426 return _gtk_css_change_for_sibling (previous_change);
430 gtk_css_selector_adjacent_compare_one (const GtkCssSelector *a,
431 const GtkCssSelector *b)
436 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
438 gtk_css_selector_adjacent_print,
439 gtk_css_selector_adjacent_match,
440 gtk_css_selector_adjacent_tree_match,
441 gtk_css_selector_adjacent_get_change,
442 gtk_css_selector_adjacent_compare_one,
443 FALSE, FALSE, FALSE, FALSE, FALSE
449 gtk_css_selector_any_print (const GtkCssSelector *selector,
452 g_string_append_c (string, '*');
456 gtk_css_selector_any_match (const GtkCssSelector *selector,
457 const GtkCssMatcher *matcher)
459 const GtkCssSelector *previous = gtk_css_selector_previous (selector);
462 previous->class == >K_CSS_SELECTOR_DESCENDANT &&
463 _gtk_css_matcher_has_regions (matcher))
465 if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
469 return gtk_css_selector_match (previous, matcher);
473 gtk_css_selector_any_tree_match (const GtkCssSelectorTree *tree,
474 const GtkCssMatcher *matcher,
477 const GtkCssSelectorTree *prev, *prev2;
479 gtk_css_selector_tree_found_match (tree, res);
481 for (prev = gtk_css_selector_tree_get_previous (tree);
483 prev = gtk_css_selector_tree_get_sibling (prev))
485 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT &&
486 _gtk_css_matcher_has_regions (matcher))
488 for (prev2 = gtk_css_selector_tree_get_previous (prev);
490 prev2 = gtk_css_selector_tree_get_sibling (prev2))
491 gtk_css_selector_tree_match (prev2, matcher, res);
494 gtk_css_selector_tree_match (prev, matcher, res);
499 gtk_css_selector_any_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
501 return previous_change;
505 gtk_css_selector_any_compare_one (const GtkCssSelector *a,
506 const GtkCssSelector *b)
511 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
513 gtk_css_selector_any_print,
514 gtk_css_selector_any_match,
515 gtk_css_selector_any_tree_match,
516 gtk_css_selector_any_get_change,
517 gtk_css_selector_any_compare_one,
518 FALSE, FALSE, FALSE, TRUE, TRUE
524 gtk_css_selector_name_print (const GtkCssSelector *selector,
527 g_string_append (string, selector->data);
531 gtk_css_selector_name_match (const GtkCssSelector *selector,
532 const GtkCssMatcher *matcher)
534 if (!_gtk_css_matcher_has_name (matcher, selector->data))
537 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
541 gtk_css_selector_name_tree_match (const GtkCssSelectorTree *tree,
542 const GtkCssMatcher *matcher,
545 const GtkCssSelectorTree *prev;
547 if (!_gtk_css_matcher_has_name (matcher, tree->selector.data))
550 gtk_css_selector_tree_found_match (tree, res);
552 for (prev = gtk_css_selector_tree_get_previous (tree);
554 prev = gtk_css_selector_tree_get_sibling (prev))
555 gtk_css_selector_tree_match (prev, matcher, res);
560 gtk_css_selector_name_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
562 return previous_change | GTK_CSS_CHANGE_NAME;
566 gtk_css_selector_name_compare_one (const GtkCssSelector *a,
567 const GtkCssSelector *b)
569 return strcmp (a->data, b->data);
572 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
574 gtk_css_selector_name_print,
575 gtk_css_selector_name_match,
576 gtk_css_selector_name_tree_match,
577 gtk_css_selector_name_get_change,
578 gtk_css_selector_name_compare_one,
579 FALSE, FALSE, TRUE, TRUE, FALSE
585 gtk_css_selector_region_print (const GtkCssSelector *selector,
588 g_string_append (string, selector->data);
592 gtk_css_selector_region_match (const GtkCssSelector *selector,
593 const GtkCssMatcher *matcher)
595 const GtkCssSelector *previous;
597 if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
600 previous = gtk_css_selector_previous (selector);
601 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
602 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
605 return gtk_css_selector_match (previous, matcher);
609 gtk_css_selector_region_tree_match (const GtkCssSelectorTree *tree,
610 const GtkCssMatcher *matcher,
613 const GtkCssSelectorTree *prev, *prev2;
615 if (!_gtk_css_matcher_has_region (matcher, tree->selector.data, 0))
618 gtk_css_selector_tree_found_match (tree, res);
620 for (prev = gtk_css_selector_tree_get_previous (tree);
622 prev = gtk_css_selector_tree_get_sibling (prev))
624 if (prev->selector.class == >K_CSS_SELECTOR_DESCENDANT)
626 for (prev2 = gtk_css_selector_tree_get_previous (prev);
628 prev2 = gtk_css_selector_tree_get_sibling (prev2))
629 gtk_css_selector_tree_match (prev2, matcher, res);
632 gtk_css_selector_tree_match (prev, matcher, res);
637 gtk_css_selector_region_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
641 change = previous_change;
642 change |= GTK_CSS_CHANGE_REGION;
643 change |= _gtk_css_change_for_child (change);
649 gtk_css_selector_region_compare_one (const GtkCssSelector *a,
650 const GtkCssSelector *b)
652 return strcmp (a->data, b->data);
655 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
657 gtk_css_selector_region_print,
658 gtk_css_selector_region_match,
659 gtk_css_selector_region_tree_match,
660 gtk_css_selector_region_get_change,
661 gtk_css_selector_region_compare_one,
662 FALSE, FALSE, TRUE, TRUE, TRUE
668 gtk_css_selector_class_print (const GtkCssSelector *selector,
671 g_string_append_c (string, '.');
672 g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (selector->data)));
676 gtk_css_selector_class_match (const GtkCssSelector *selector,
677 const GtkCssMatcher *matcher)
679 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (selector->data)))
682 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
686 gtk_css_selector_class_tree_match (const GtkCssSelectorTree *tree,
687 const GtkCssMatcher *matcher,
690 const GtkCssSelectorTree *prev;
692 if (!_gtk_css_matcher_has_class (matcher, GPOINTER_TO_UINT (tree->selector.data)))
695 gtk_css_selector_tree_found_match (tree, res);
697 for (prev = gtk_css_selector_tree_get_previous (tree);
699 prev = gtk_css_selector_tree_get_sibling (prev))
700 gtk_css_selector_tree_match (prev, matcher, res);
704 gtk_css_selector_class_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
706 return previous_change | GTK_CSS_CHANGE_CLASS;
710 gtk_css_selector_class_compare_one (const GtkCssSelector *a,
711 const GtkCssSelector *b)
713 return strcmp (g_quark_to_string (GPOINTER_TO_UINT (a->data)),
714 g_quark_to_string (GPOINTER_TO_UINT (b->data)));
717 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
719 gtk_css_selector_class_print,
720 gtk_css_selector_class_match,
721 gtk_css_selector_class_tree_match,
722 gtk_css_selector_class_get_change,
723 gtk_css_selector_class_compare_one,
724 FALSE, TRUE, FALSE, TRUE, FALSE
730 gtk_css_selector_id_print (const GtkCssSelector *selector,
733 g_string_append_c (string, '#');
734 g_string_append (string, selector->data);
738 gtk_css_selector_id_match (const GtkCssSelector *selector,
739 const GtkCssMatcher *matcher)
741 if (!_gtk_css_matcher_has_id (matcher, selector->data))
744 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
748 gtk_css_selector_id_tree_match (const GtkCssSelectorTree *tree,
749 const GtkCssMatcher *matcher,
752 const GtkCssSelectorTree *prev;
754 if (!_gtk_css_matcher_has_id (matcher, tree->selector.data))
757 gtk_css_selector_tree_found_match (tree, res);
759 for (prev = gtk_css_selector_tree_get_previous (tree);
761 prev = gtk_css_selector_tree_get_sibling (prev))
762 gtk_css_selector_tree_match (prev, matcher, res);
766 gtk_css_selector_id_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
768 return previous_change | GTK_CSS_CHANGE_ID;
773 gtk_css_selector_id_compare_one (const GtkCssSelector *a,
774 const GtkCssSelector *b)
776 return strcmp (a->data, b->data);
779 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
781 gtk_css_selector_id_print,
782 gtk_css_selector_id_match,
783 gtk_css_selector_id_tree_match,
784 gtk_css_selector_id_get_change,
785 gtk_css_selector_id_compare_one,
786 TRUE, FALSE, FALSE, TRUE, FALSE
789 /* PSEUDOCLASS FOR STATE */
792 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
795 static const char * state_names[] = {
806 state = GPOINTER_TO_UINT (selector->data);
807 g_string_append_c (string, ':');
809 for (i = 0; i < G_N_ELEMENTS (state_names); i++)
811 if (state == (1 << i))
813 g_string_append (string, state_names[i]);
818 g_assert_not_reached ();
822 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
823 const GtkCssMatcher *matcher)
825 GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
827 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
830 return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
834 gtk_css_selector_pseudoclass_state_tree_match (const GtkCssSelectorTree *tree,
835 const GtkCssMatcher *matcher,
838 GtkStateFlags state = GPOINTER_TO_UINT (tree->selector.data);
839 const GtkCssSelectorTree *prev;
841 if ((_gtk_css_matcher_get_state (matcher) & state) != state)
844 gtk_css_selector_tree_found_match (tree, res);
846 for (prev = gtk_css_selector_tree_get_previous (tree);
848 prev = gtk_css_selector_tree_get_sibling (prev))
849 gtk_css_selector_tree_match (prev, matcher, res);
854 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
856 return previous_change | GTK_CSS_CHANGE_STATE;
860 gtk_css_selector_pseudoclass_state_compare_one (const GtkCssSelector *a,
861 const GtkCssSelector *b)
863 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
866 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
868 gtk_css_selector_pseudoclass_state_print,
869 gtk_css_selector_pseudoclass_state_match,
870 gtk_css_selector_pseudoclass_state_tree_match,
871 gtk_css_selector_pseudoclass_state_get_change,
872 gtk_css_selector_pseudoclass_state_compare_one,
873 FALSE, TRUE, FALSE, TRUE, FALSE
876 /* PSEUDOCLASS FOR POSITION */
884 #define POSITION_TYPE_BITS 2
885 #define POSITION_NUMBER_BITS ((sizeof (gpointer) * 8 - POSITION_TYPE_BITS) / 2)
888 encode_position (PositionType type,
895 gssize type :POSITION_TYPE_BITS;
896 gssize a :POSITION_NUMBER_BITS;
897 gssize b :POSITION_NUMBER_BITS;
900 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
902 g_assert (type < (1 << POSITION_TYPE_BITS));
904 result.data.type = type;
912 decode_position (const GtkCssSelector *selector,
920 gssize type :POSITION_TYPE_BITS;
921 gssize a :POSITION_NUMBER_BITS;
922 gssize b :POSITION_NUMBER_BITS;
925 G_STATIC_ASSERT (sizeof (gconstpointer) == sizeof (result));
927 result.p = selector->data;
929 *type = result.data.type & ((1 << POSITION_TYPE_BITS) - 1);
935 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
941 decode_position (selector, &type, &a, &b);
944 case POSITION_FORWARD:
948 g_string_append (string, ":first-child");
950 g_string_append_printf (string, ":nth-child(%d)", b);
952 else if (a == 2 && b == 0)
953 g_string_append (string, ":nth-child(even)");
954 else if (a == 2 && b == 1)
955 g_string_append (string, ":nth-child(odd)");
958 g_string_append (string, ":nth-child(");
960 g_string_append (string, "n");
962 g_string_append (string, "-n");
964 g_string_append_printf (string, "%dn", a);
966 g_string_append_printf (string, "+%d)", b);
968 g_string_append_printf (string, "%d)", b);
970 g_string_append (string, ")");
973 case POSITION_BACKWARD:
977 g_string_append (string, ":last-child");
979 g_string_append_printf (string, ":nth-last-child(%d)", b);
981 else if (a == 2 && b == 0)
982 g_string_append (string, ":nth-last-child(even)");
983 else if (a == 2 && b == 1)
984 g_string_append (string, ":nth-last-child(odd)");
987 g_string_append (string, ":nth-last-child(");
989 g_string_append (string, "n");
991 g_string_append (string, "-n");
993 g_string_append_printf (string, "%dn", a);
995 g_string_append_printf (string, "+%d)", b);
997 g_string_append_printf (string, "%d)", b);
999 g_string_append (string, ")");
1003 g_string_append (string, ":only-child");
1005 case POSITION_SORTED:
1006 g_string_append (string, ":sorted");
1009 g_assert_not_reached ();
1015 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
1016 const GtkCssMatcher *matcher)
1018 GtkRegionFlags selector_flags;
1019 const GtkCssSelector *previous;
1023 decode_position (selector, &type, &a, &b);
1026 case POSITION_FORWARD:
1027 if (a == 0 && b == 1)
1028 selector_flags = GTK_REGION_FIRST;
1029 else if (a == 2 && b == 0)
1030 selector_flags = GTK_REGION_EVEN;
1031 else if (a == 2 && b == 1)
1032 selector_flags = GTK_REGION_ODD;
1036 case POSITION_BACKWARD:
1037 if (a == 0 && b == 1)
1038 selector_flags = GTK_REGION_LAST;
1043 selector_flags = GTK_REGION_ONLY;
1045 case POSITION_SORTED:
1046 selector_flags = GTK_REGION_SORTED;
1049 g_assert_not_reached ();
1052 selector = gtk_css_selector_previous (selector);
1054 if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
1057 previous = gtk_css_selector_previous (selector);
1058 if (previous && previous->class == >K_CSS_SELECTOR_DESCENDANT &&
1059 gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
1062 return gtk_css_selector_match (previous, matcher);
1066 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
1067 const GtkCssMatcher *matcher)
1069 const GtkCssSelector *previous;
1073 previous = gtk_css_selector_previous (selector);
1074 if (previous && previous->class == >K_CSS_SELECTOR_REGION)
1075 return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
1077 decode_position (selector, &type, &a, &b);
1080 case POSITION_FORWARD:
1081 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1084 case POSITION_BACKWARD:
1085 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1089 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1090 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1093 case POSITION_SORTED:
1096 g_assert_not_reached ();
1100 return gtk_css_selector_match (previous, matcher);
1104 gtk_css_selector_pseudoclass_position_tree_match_for_region (const GtkCssSelectorTree *tree,
1105 const GtkCssSelectorTree *prev,
1106 const GtkCssMatcher *matcher,
1109 const GtkCssSelectorTree *prev2;
1110 GtkRegionFlags selector_flags;
1114 decode_position (&tree->selector, &type, &a, &b);
1117 case POSITION_FORWARD:
1118 if (a == 0 && b == 1)
1119 selector_flags = GTK_REGION_FIRST;
1120 else if (a == 2 && b == 0)
1121 selector_flags = GTK_REGION_EVEN;
1122 else if (a == 2 && b == 1)
1123 selector_flags = GTK_REGION_ODD;
1127 case POSITION_BACKWARD:
1128 if (a == 0 && b == 1)
1129 selector_flags = GTK_REGION_LAST;
1134 selector_flags = GTK_REGION_ONLY;
1136 case POSITION_SORTED:
1137 selector_flags = GTK_REGION_SORTED;
1140 g_assert_not_reached ();
1144 if (!_gtk_css_matcher_has_region (matcher, prev->selector.data, selector_flags))
1147 gtk_css_selector_tree_found_match (prev, res);
1149 for (prev2 = gtk_css_selector_tree_get_previous (prev);
1151 prev2 = gtk_css_selector_tree_get_sibling (prev2))
1153 if (prev2->selector.class == >K_CSS_SELECTOR_DESCENDANT)
1154 gtk_css_selector_tree_match (gtk_css_selector_tree_get_previous (prev2), matcher, res);
1155 gtk_css_selector_tree_match (prev2, matcher, res);
1160 gtk_css_selector_pseudoclass_position_tree_match (const GtkCssSelectorTree *tree,
1161 const GtkCssMatcher *matcher,
1164 const GtkCssSelectorTree *prev;
1168 for (prev = gtk_css_selector_tree_get_previous (tree);
1170 prev = gtk_css_selector_tree_get_sibling (prev))
1172 if (prev->selector.class == >K_CSS_SELECTOR_REGION)
1173 gtk_css_selector_pseudoclass_position_tree_match_for_region (tree, prev, matcher, res);
1176 decode_position (&tree->selector, &type, &a, &b);
1179 case POSITION_FORWARD:
1180 if (!_gtk_css_matcher_has_position (matcher, TRUE, a, b))
1183 case POSITION_BACKWARD:
1184 if (!_gtk_css_matcher_has_position (matcher, FALSE, a, b))
1188 if (!_gtk_css_matcher_has_position (matcher, TRUE, 0, 1) ||
1189 !_gtk_css_matcher_has_position (matcher, FALSE, 0, 1))
1192 case POSITION_SORTED:
1195 g_assert_not_reached ();
1199 gtk_css_selector_tree_found_match (tree, res);
1201 for (prev = gtk_css_selector_tree_get_previous (tree); prev != NULL; prev = gtk_css_selector_tree_get_sibling (prev))
1203 if (prev->selector.class != >K_CSS_SELECTOR_REGION)
1204 gtk_css_selector_tree_match (prev, matcher, res);
1209 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector, GtkCssChange previous_change)
1211 return previous_change | GTK_CSS_CHANGE_POSITION;
1215 gtk_css_selector_pseudoclass_position_compare_one (const GtkCssSelector *a,
1216 const GtkCssSelector *b)
1218 return GPOINTER_TO_UINT (a->data) - GPOINTER_TO_UINT (b->data);
1221 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
1222 "pseudoclass-position",
1223 gtk_css_selector_pseudoclass_position_print,
1224 gtk_css_selector_pseudoclass_position_match,
1225 gtk_css_selector_pseudoclass_position_tree_match,
1226 gtk_css_selector_pseudoclass_position_get_change,
1227 gtk_css_selector_pseudoclass_position_compare_one,
1228 FALSE, TRUE, FALSE, TRUE, TRUE
1234 gtk_css_selector_size (const GtkCssSelector *selector)
1240 selector = gtk_css_selector_previous (selector);
1247 static GtkCssSelector *
1248 gtk_css_selector_new (const GtkCssSelectorClass *class,
1249 GtkCssSelector *selector,
1254 size = gtk_css_selector_size (selector);
1255 selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
1257 selector[1].class = NULL;
1259 memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
1261 selector->class = class;
1262 selector->data = data;
1267 static GtkCssSelector *
1268 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
1272 name = _gtk_css_parser_try_name (parser, FALSE);
1276 _gtk_css_parser_error (parser, "Expected a valid name for class");
1278 _gtk_css_selector_free (selector);
1282 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CLASS,
1284 GUINT_TO_POINTER (g_quark_from_string (name)));
1291 static GtkCssSelector *
1292 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
1296 name = _gtk_css_parser_try_name (parser, FALSE);
1300 _gtk_css_parser_error (parser, "Expected a valid name for id");
1302 _gtk_css_selector_free (selector);
1306 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID,
1308 g_intern_string (name));
1315 static GtkCssSelector *
1316 parse_selector_pseudo_class_nth_child (GtkCssParser *parser,
1317 GtkCssSelector *selector,
1322 if (!_gtk_css_parser_try (parser, "(", TRUE))
1324 _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class");
1326 _gtk_css_selector_free (selector);
1330 if (_gtk_css_parser_try (parser, "even", TRUE))
1335 else if (_gtk_css_parser_try (parser, "odd", TRUE))
1340 else if (type == POSITION_FORWARD &&
1341 _gtk_css_parser_try (parser, "first", TRUE))
1346 else if (type == POSITION_FORWARD &&
1347 _gtk_css_parser_try (parser, "last", TRUE))
1351 type = POSITION_BACKWARD;
1357 if (_gtk_css_parser_try (parser, "+", TRUE))
1359 else if (_gtk_css_parser_try (parser, "-", TRUE))
1364 if (_gtk_css_parser_try_int (parser, &a))
1368 _gtk_css_parser_error (parser, "Expected an integer");
1370 _gtk_css_selector_free (selector);
1375 else if (_gtk_css_parser_has_prefix (parser, "n"))
1381 _gtk_css_parser_error (parser, "Expected an integer");
1383 _gtk_css_selector_free (selector);
1387 if (_gtk_css_parser_try (parser, "n", TRUE))
1389 if (_gtk_css_parser_try (parser, "+", TRUE))
1391 else if (_gtk_css_parser_try (parser, "-", TRUE))
1396 if (_gtk_css_parser_try_int (parser, &b))
1400 _gtk_css_parser_error (parser, "Expected an integer");
1402 _gtk_css_selector_free (selector);
1418 if (!_gtk_css_parser_try (parser, ")", FALSE))
1420 _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
1422 _gtk_css_selector_free (selector);
1426 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1428 encode_position (type, a, b));
1433 static GtkCssSelector *
1434 parse_selector_pseudo_class (GtkCssParser *parser,
1435 GtkCssSelector *selector)
1437 static const struct {
1439 GtkStateFlags state_flag;
1440 PositionType position_type;
1443 } pseudo_classes[] = {
1444 { "first-child", 0, POSITION_FORWARD, 0, 1 },
1445 { "last-child", 0, POSITION_BACKWARD, 0, 1 },
1446 { "only-child", 0, POSITION_ONLY, 0, 0 },
1447 { "sorted", 0, POSITION_SORTED, 0, 0 },
1448 { "active", GTK_STATE_FLAG_ACTIVE, },
1449 { "prelight", GTK_STATE_FLAG_PRELIGHT, },
1450 { "hover", GTK_STATE_FLAG_PRELIGHT, },
1451 { "selected", GTK_STATE_FLAG_SELECTED, },
1452 { "insensitive", GTK_STATE_FLAG_INSENSITIVE, },
1453 { "inconsistent", GTK_STATE_FLAG_INCONSISTENT, },
1454 { "focused", GTK_STATE_FLAG_FOCUSED, },
1455 { "focus", GTK_STATE_FLAG_FOCUSED, },
1456 { "backdrop", GTK_STATE_FLAG_BACKDROP, }
1460 if (_gtk_css_parser_try (parser, "nth-child", FALSE))
1461 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD);
1462 else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE))
1463 return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD);
1465 for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++)
1467 if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
1469 if (pseudo_classes[i].state_flag)
1470 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_STATE,
1472 GUINT_TO_POINTER (pseudo_classes[i].state_flag));
1474 selector = gtk_css_selector_new (>K_CSS_SELECTOR_PSEUDOCLASS_POSITION,
1476 encode_position (pseudo_classes[i].position_type,
1477 pseudo_classes[i].position_a,
1478 pseudo_classes[i].position_b));
1483 _gtk_css_parser_error (parser, "Missing name of pseudo-class");
1485 _gtk_css_selector_free (selector);
1489 static GtkCssSelector *
1490 try_parse_name (GtkCssParser *parser,
1491 GtkCssSelector *selector)
1495 name = _gtk_css_parser_try_ident (parser, FALSE);
1498 selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
1499 ? >K_CSS_SELECTOR_REGION
1500 : >K_CSS_SELECTOR_NAME,
1502 g_intern_string (name));
1505 else if (_gtk_css_parser_try (parser, "*", FALSE))
1506 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector, NULL);
1511 static GtkCssSelector *
1512 parse_simple_selector (GtkCssParser *parser,
1513 GtkCssSelector *selector)
1515 guint size = gtk_css_selector_size (selector);
1517 selector = try_parse_name (parser, selector);
1520 if (_gtk_css_parser_try (parser, "#", FALSE))
1521 selector = parse_selector_id (parser, selector);
1522 else if (_gtk_css_parser_try (parser, ".", FALSE))
1523 selector = parse_selector_class (parser, selector);
1524 else if (_gtk_css_parser_try (parser, ":", FALSE))
1525 selector = parse_selector_pseudo_class (parser, selector);
1526 else if (gtk_css_selector_size (selector) == size)
1528 _gtk_css_parser_error (parser, "Expected a valid selector");
1530 _gtk_css_selector_free (selector);
1536 while (selector && !_gtk_css_parser_is_eof (parser));
1538 _gtk_css_parser_skip_whitespace (parser);
1544 _gtk_css_selector_parse (GtkCssParser *parser)
1546 GtkCssSelector *selector = NULL;
1548 while ((selector = parse_simple_selector (parser, selector)) &&
1549 !_gtk_css_parser_is_eof (parser) &&
1550 !_gtk_css_parser_begins_with (parser, ',') &&
1551 !_gtk_css_parser_begins_with (parser, '{'))
1553 if (_gtk_css_parser_try (parser, "+", TRUE))
1554 selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector, NULL);
1555 else if (_gtk_css_parser_try (parser, "~", TRUE))
1556 selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector, NULL);
1557 else if (_gtk_css_parser_try (parser, ">", TRUE))
1558 selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector, NULL);
1560 selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector, NULL);
1567 _gtk_css_selector_free (GtkCssSelector *selector)
1569 g_return_if_fail (selector != NULL);
1575 _gtk_css_selector_print (const GtkCssSelector *selector,
1578 const GtkCssSelector *previous;
1580 g_return_if_fail (selector != NULL);
1582 previous = gtk_css_selector_previous (selector);
1584 _gtk_css_selector_print (previous, str);
1586 selector->class->print (selector, str);
1590 _gtk_css_selector_to_string (const GtkCssSelector *selector)
1594 g_return_val_if_fail (selector != NULL, NULL);
1596 string = g_string_new (NULL);
1598 _gtk_css_selector_print (selector, string);
1600 return g_string_free (string, FALSE);
1605 _gtk_css_selector_tree_match_get_change (const GtkCssSelectorTree *tree)
1607 GtkCssChange change = 0;
1611 change = tree->selector.class->get_change (&tree->selector, change);
1612 tree = gtk_css_selector_tree_get_parent (tree);
1619 * _gtk_css_selector_matches:
1620 * @selector: the selector
1621 * @path: the path to check
1622 * @state: The state to match
1624 * Checks if the @selector matches the given @path. If @length is
1625 * smaller than the number of elements in @path, it is assumed that
1626 * only the first @length element of @path are valid and the rest
1627 * does not exist. This is useful for doing parent matches for the
1628 * 'inherit' keyword.
1630 * Returns: %TRUE if the selector matches @path
1633 _gtk_css_selector_matches (const GtkCssSelector *selector,
1634 const GtkCssMatcher *matcher)
1637 g_return_val_if_fail (selector != NULL, FALSE);
1638 g_return_val_if_fail (matcher != NULL, FALSE);
1640 return gtk_css_selector_match (selector, matcher);
1643 /* Computes specificity according to CSS 2.1.
1644 * The arguments must be initialized to 0 */
1646 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
1651 for (; selector; selector = gtk_css_selector_previous (selector))
1653 const GtkCssSelectorClass *klass = selector->class;
1655 if (klass->increase_id_specificity)
1657 if (klass->increase_class_specificity)
1659 if (klass->increase_element_specificity)
1665 _gtk_css_selector_compare (const GtkCssSelector *a,
1666 const GtkCssSelector *b)
1668 guint a_ids = 0, a_classes = 0, a_elements = 0;
1669 guint b_ids = 0, b_classes = 0, b_elements = 0;
1672 _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
1673 _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
1675 compare = a_ids - b_ids;
1679 compare = a_classes - b_classes;
1683 return a_elements - b_elements;
1687 /******************** SelectorTree handling *****************/
1690 gtk_css_selectors_count_initial_init (void)
1692 return g_hash_table_new ((GHashFunc)gtk_css_selector_hash, (GEqualFunc)gtk_css_selector_equal);
1696 gtk_css_selectors_count_initial (const GtkCssSelector *selector, GHashTable *hash)
1698 if (!selector->class->is_simple || selector->class->must_keep_order)
1700 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1701 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1706 selector && selector->class->is_simple && !selector->class->must_keep_order;
1707 selector = gtk_css_selector_previous (selector))
1709 guint count = GPOINTER_TO_INT (g_hash_table_lookup (hash, selector));
1710 g_hash_table_replace (hash, (gpointer)selector, GUINT_TO_POINTER (count + 1));
1715 gtk_css_selectors_has_initial_selector (const GtkCssSelector *selector, const GtkCssSelector *initial)
1717 if (!selector->class->is_simple || selector->class->must_keep_order)
1718 return gtk_css_selector_equal (selector, initial);
1721 selector && selector->class->is_simple && !selector->class->must_keep_order;
1722 selector = gtk_css_selector_previous (selector))
1724 if (gtk_css_selector_equal (selector, initial))
1731 static GtkCssSelector *
1732 gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssSelector *initial)
1734 GtkCssSelector *found;
1737 /* If the initial simple selector is not first, move it there so we can skip it
1738 without losing any other selectors */
1739 if (!gtk_css_selector_equal (selector, initial))
1741 for (found = selector; found && found->class->is_simple; found = (GtkCssSelector *)gtk_css_selector_previous (found))
1743 if (gtk_css_selector_equal (found, initial))
1747 g_assert (found != NULL && found->class->is_simple);
1754 return (GtkCssSelector *)gtk_css_selector_previous (selector);
1758 direct_ptr_compare (const void *_a, const void *_b)
1760 gpointer *a = (gpointer *)_a;
1761 gpointer *b = (gpointer *)_b;
1770 _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
1771 const GtkCssMatcher *matcher)
1775 GHashTableIter iter;
1778 res = g_hash_table_new (g_direct_hash, g_direct_equal);
1780 for (; tree != NULL;
1781 tree = gtk_css_selector_tree_get_sibling (tree))
1782 gtk_css_selector_tree_match (tree, matcher, res);
1784 array = g_ptr_array_sized_new (g_hash_table_size (res));
1786 g_hash_table_iter_init (&iter, res);
1787 while (g_hash_table_iter_next (&iter, &key, NULL))
1788 g_ptr_array_add (array, key);
1790 g_hash_table_destroy (res);
1792 qsort (array->pdata, array->len, sizeof (gpointer), direct_ptr_compare);
1799 _gtk_css_selector_tree_print (GtkCssSelectorTree *tree, GString *str, char *prefix)
1801 gboolean first = TRUE;
1804 for (; tree != NULL; tree = tree->siblings, first = FALSE)
1807 g_string_append (str, prefix);
1812 g_string_append (str, "─┬─");
1814 g_string_append (str, "───");
1819 g_string_append (str, " ├─");
1821 g_string_append (str, " └─");
1825 tree->selector.class->print (&tree->selector, str);
1826 len = str->len - len;
1828 if (gtk_css_selector_tree_get_previous (tree))
1830 GString *prefix2 = g_string_new (prefix);
1833 g_string_append (prefix2, " │ ");
1835 g_string_append (prefix2, " ");
1836 for (i = 0; i < len; i++)
1837 g_string_append_c (prefix2, ' ');
1839 _gtk_css_selector_tree_print (gtk_css_selector_tree_get_previous (tree), str, prefix2->str);
1840 g_string_free (prefix2, TRUE);
1843 g_string_append (str, "\n");
1849 _gtk_css_selector_tree_match_print (const GtkCssSelectorTree *tree,
1852 const GtkCssSelectorTree *parent;
1854 g_return_if_fail (tree != NULL);
1856 tree->selector.class->print (&tree->selector, str);
1858 parent = gtk_css_selector_tree_get_parent (tree);
1860 _gtk_css_selector_tree_match_print (parent, str);
1864 _gtk_css_selector_tree_free (GtkCssSelectorTree *tree)
1875 GtkCssSelector *current_selector;
1876 GtkCssSelectorTree **selector_match;
1877 } GtkCssSelectorRuleSetInfo;
1879 static GtkCssSelectorTree *
1880 get_tree (GByteArray *array, gint32 offset)
1882 return (GtkCssSelectorTree *) (array->data + offset);
1885 static GtkCssSelectorTree *
1886 alloc_tree (GByteArray *array, gint32 *offset)
1888 GtkCssSelectorTree tree = { { NULL} };
1890 *offset = array->len;
1891 g_byte_array_append (array, (guint8 *)&tree, sizeof (GtkCssSelectorTree));
1892 return get_tree (array, *offset);
1896 subdivide_infos (GByteArray *array, GList *infos, gint32 parent_offset)
1903 GtkCssSelectorTree *tree;
1904 GtkCssSelectorRuleSetInfo *info;
1905 GtkCssSelector max_selector;
1906 GHashTableIter iter;
1908 gpointer key, value;
1909 GPtrArray *exact_matches;
1913 return GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
1915 ht = gtk_css_selectors_count_initial_init ();
1917 for (l = infos; l != NULL; l = l->next)
1920 gtk_css_selectors_count_initial (info->current_selector, ht);
1923 /* Pick the selector with highest count, and use as decision on this level
1924 as that makes it possible to skip the largest amount of checks later */
1928 g_hash_table_iter_init (&iter, ht);
1929 while (g_hash_table_iter_next (&iter, &key, &value))
1931 GtkCssSelector *selector = key;
1932 if (GPOINTER_TO_UINT (value) > max_count ||
1933 (GPOINTER_TO_UINT (value) == max_count &&
1934 gtk_css_selector_compare_one (selector, &max_selector) < 0))
1936 max_count = GPOINTER_TO_UINT (value);
1937 max_selector = *selector;
1944 tree = alloc_tree (array, &tree_offset);
1945 tree->parent_offset = parent_offset;
1946 tree->selector = max_selector;
1948 exact_matches = NULL;
1949 for (l = infos; l != NULL; l = l->next)
1953 if (gtk_css_selectors_has_initial_selector (info->current_selector, &max_selector))
1955 info->current_selector = gtk_css_selectors_skip_initial_selector (info->current_selector, &max_selector);
1956 if (info->current_selector == NULL)
1958 /* Matches current node */
1959 if (exact_matches == NULL)
1960 exact_matches = g_ptr_array_new ();
1961 g_ptr_array_add (exact_matches, info->match);
1962 if (info->selector_match != NULL)
1963 *info->selector_match = GUINT_TO_POINTER (tree_offset);
1966 matched = g_list_prepend (matched, info);
1970 remaining = g_list_prepend (remaining, info);
1976 g_ptr_array_add (exact_matches, NULL); /* Null terminate */
1978 g_byte_array_append (array, (guint8 *)exact_matches->pdata,
1979 exact_matches->len * sizeof (gpointer));
1980 g_ptr_array_free (exact_matches, TRUE);
1983 res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
1984 get_tree (array, tree_offset)->matches_offset = res;
1986 res = subdivide_infos (array, matched, tree_offset);
1987 get_tree (array, tree_offset)->previous_offset = res;
1989 res = subdivide_infos (array, remaining, parent_offset);
1990 get_tree (array, tree_offset)->sibling_offset = res;
1992 g_list_free (matched);
1993 g_list_free (remaining);
1994 g_hash_table_unref (ht);
1999 struct _GtkCssSelectorTreeBuilder {
2003 GtkCssSelectorTreeBuilder *
2004 _gtk_css_selector_tree_builder_new (void)
2006 return g_new0 (GtkCssSelectorTreeBuilder, 1);
2010 _gtk_css_selector_tree_builder_free (GtkCssSelectorTreeBuilder *builder)
2012 g_list_free_full (builder->infos, g_free);
2017 _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
2018 GtkCssSelector *selectors,
2019 GtkCssSelectorTree **selector_match,
2022 GtkCssSelectorRuleSetInfo *info = g_new0 (GtkCssSelectorRuleSetInfo, 1);
2024 info->match = match;
2025 info->current_selector = selectors;
2026 info->selector_match = selector_match;
2027 builder->infos = g_list_prepend (builder->infos, info);
2030 /* Convert all offsets to node-relative */
2032 fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
2034 while (tree != NULL)
2036 if (tree->parent_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2037 tree->parent_offset -= ((guint8 *)tree - data);
2039 if (tree->previous_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2040 tree->previous_offset -= ((guint8 *)tree - data);
2042 if (tree->sibling_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2043 tree->sibling_offset -= ((guint8 *)tree - data);
2045 if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
2046 tree->matches_offset -= ((guint8 *)tree - data);
2048 fixup_offsets ((GtkCssSelectorTree *)gtk_css_selector_tree_get_previous (tree), data);
2050 tree = (GtkCssSelectorTree *)gtk_css_selector_tree_get_sibling (tree);
2054 GtkCssSelectorTree *
2055 _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
2057 GtkCssSelectorTree *tree;
2062 GtkCssSelectorRuleSetInfo *info;
2064 array = g_byte_array_new ();
2065 subdivide_infos (array, builder->infos, GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET);
2068 data = g_byte_array_free (array, FALSE);
2070 /* shrink to final size */
2071 data = g_realloc (data, len);
2073 tree = (GtkCssSelectorTree *)data;
2075 fixup_offsets (tree, data);
2077 /* Convert offsets to final pointers */
2078 for (l = builder->infos; l != NULL; l = l->next)
2081 if (info->selector_match)
2082 *info->selector_match = (GtkCssSelectorTree *)(data + GPOINTER_TO_UINT (*info->selector_match));
2087 GString *s = g_string_new ("");
2088 _gtk_css_selector_tree_print (tree, s, "");
2089 g_print ("%s", s->str);
2090 g_string_free (s, TRUE);