]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssselector.c
selector: Rename selector class
[~andy/gtk] / gtk / gtkcssselector.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #include "config.h"
19
20 #include "gtkcssselectorprivate.h"
21
22 #include <string.h>
23
24 #include "gtkcssprovider.h"
25 #include "gtkstylecontextprivate.h"
26
27 typedef struct _GtkCssSelectorClass GtkCssSelectorClass;
28
29 struct _GtkCssSelectorClass {
30   const char        *name;
31
32   void              (* print)       (const GtkCssSelector       *selector,
33                                      GString                    *string);
34   gboolean          (* match)       (const GtkCssSelector       *selector,
35                                      const GtkCssMatcher        *matcher);
36   GtkCssChange      (* get_change)  (const GtkCssSelector       *selector);
37
38   guint         increase_id_specificity :1;
39   guint         increase_class_specificity :1;
40   guint         increase_element_specificity :1;
41 };
42
43 struct _GtkCssSelector
44 {
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 */
49 };
50
51 static gboolean
52 gtk_css_selector_match (const GtkCssSelector *selector,
53                         const GtkCssMatcher  *matcher)
54 {
55   if (selector == NULL)
56     return TRUE;
57
58   return selector->class->match (selector, matcher);
59 }
60
61 static GtkCssChange
62 gtk_css_selector_get_change (const GtkCssSelector *selector)
63 {
64   if (selector == NULL)
65     return 0;
66
67   return selector->class->get_change (selector);
68 }
69
70 static const GtkCssSelector *
71 gtk_css_selector_previous (const GtkCssSelector *selector)
72 {
73   selector = selector + 1;
74
75   return selector->class ? selector : NULL;
76 }
77
78 /* DESCENDANT */
79
80 static void
81 gtk_css_selector_descendant_print (const GtkCssSelector *selector,
82                                    GString              *string)
83 {
84   g_string_append_c (string, ' ');
85 }
86
87 static gboolean
88 gtk_css_selector_descendant_match (const GtkCssSelector *selector,
89                                    const GtkCssMatcher  *matcher)
90 {
91   GtkCssMatcher ancestor;
92
93   while (_gtk_css_matcher_get_parent (&ancestor, matcher))
94     {
95       matcher = &ancestor;
96
97       if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
98         return TRUE;
99     }
100
101   return FALSE;
102 }
103
104 static GtkCssChange
105 gtk_css_selector_descendant_get_change (const GtkCssSelector *selector)
106 {
107   return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
108 }
109
110 static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
111   "descendant",
112   gtk_css_selector_descendant_print,
113   gtk_css_selector_descendant_match,
114   gtk_css_selector_descendant_get_change,
115   FALSE, FALSE, FALSE
116 };
117
118 /* CHILD */
119
120 static void
121 gtk_css_selector_child_print (const GtkCssSelector *selector,
122                               GString              *string)
123 {
124   g_string_append (string, " > ");
125 }
126
127 static gboolean
128 gtk_css_selector_child_match (const GtkCssSelector *selector,
129                               const GtkCssMatcher  *matcher)
130 {
131   GtkCssMatcher parent;
132
133   if (!_gtk_css_matcher_get_parent (&parent, matcher))
134     return FALSE;
135
136   return gtk_css_selector_match (gtk_css_selector_previous (selector), &parent);
137 }
138
139 static GtkCssChange
140 gtk_css_selector_child_get_change (const GtkCssSelector *selector)
141 {
142   return _gtk_css_change_for_child (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
143 }
144
145 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
146   "child",
147   gtk_css_selector_child_print,
148   gtk_css_selector_child_match,
149   gtk_css_selector_child_get_change,
150   FALSE, FALSE, FALSE
151 };
152
153 /* SIBLING */
154
155 static void
156 gtk_css_selector_sibling_print (const GtkCssSelector *selector,
157                                 GString              *string)
158 {
159   g_string_append (string, " ~ ");
160 }
161
162 static gboolean
163 gtk_css_selector_sibling_match (const GtkCssSelector *selector,
164                                 const GtkCssMatcher  *matcher)
165 {
166   GtkCssMatcher previous;
167
168   while (_gtk_css_matcher_get_previous (&previous, matcher))
169     {
170       matcher = &previous;
171
172       if (gtk_css_selector_match (gtk_css_selector_previous (selector), matcher))
173         return TRUE;
174     }
175
176   return FALSE;
177 }
178
179 static GtkCssChange
180 gtk_css_selector_sibling_get_change (const GtkCssSelector *selector)
181 {
182   return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
183 }
184
185 static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
186   "sibling",
187   gtk_css_selector_sibling_print,
188   gtk_css_selector_sibling_match,
189   gtk_css_selector_sibling_get_change,
190   FALSE, FALSE, FALSE
191 };
192
193 /* ADJACENT */
194
195 static void
196 gtk_css_selector_adjacent_print (const GtkCssSelector *selector,
197                                  GString              *string)
198 {
199   g_string_append (string, " + ");
200 }
201
202 static gboolean
203 gtk_css_selector_adjacent_match (const GtkCssSelector *selector,
204                                  const GtkCssMatcher  *matcher)
205 {
206   GtkCssMatcher previous;
207
208   if (!_gtk_css_matcher_get_previous (&previous, matcher))
209     return FALSE;
210
211   return gtk_css_selector_match (gtk_css_selector_previous (selector), &previous);
212 }
213
214 static GtkCssChange
215 gtk_css_selector_adjacent_get_change (const GtkCssSelector *selector)
216 {
217   return _gtk_css_change_for_sibling (gtk_css_selector_get_change (gtk_css_selector_previous (selector)));
218 }
219
220 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
221   "adjacent",
222   gtk_css_selector_adjacent_print,
223   gtk_css_selector_adjacent_match,
224   gtk_css_selector_adjacent_get_change,
225   FALSE, FALSE, FALSE
226 };
227
228 /* ANY */
229
230 static void
231 gtk_css_selector_any_print (const GtkCssSelector *selector,
232                             GString              *string)
233 {
234   g_string_append_c (string, '*');
235 }
236
237 static gboolean
238 gtk_css_selector_any_match (const GtkCssSelector *selector,
239                             const GtkCssMatcher  *matcher)
240 {
241   const GtkCssSelector *previous = gtk_css_selector_previous (selector);
242   
243   if (previous &&
244       previous->class == &GTK_CSS_SELECTOR_DESCENDANT &&
245       _gtk_css_matcher_has_regions (matcher))
246     {
247       if (gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
248         return TRUE;
249     }
250   
251   return gtk_css_selector_match (previous, matcher);
252 }
253
254 static GtkCssChange
255 gtk_css_selector_any_get_change (const GtkCssSelector *selector)
256 {
257   return gtk_css_selector_get_change (gtk_css_selector_previous (selector));
258 }
259
260 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
261   "any",
262   gtk_css_selector_any_print,
263   gtk_css_selector_any_match,
264   gtk_css_selector_any_get_change,
265   FALSE, FALSE, FALSE
266 };
267
268 /* NAME */
269
270 static void
271 gtk_css_selector_name_print (const GtkCssSelector *selector,
272                              GString              *string)
273 {
274   g_string_append (string, selector->data);
275 }
276
277 static gboolean
278 gtk_css_selector_name_match (const GtkCssSelector *selector,
279                              const GtkCssMatcher  *matcher)
280 {
281   if (!_gtk_css_matcher_has_name (matcher, selector->data))
282     return FALSE;
283
284   return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
285 }
286
287 static GtkCssChange
288 gtk_css_selector_name_get_change (const GtkCssSelector *selector)
289 {
290   return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_NAME;
291 }
292
293 static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
294   "name",
295   gtk_css_selector_name_print,
296   gtk_css_selector_name_match,
297   gtk_css_selector_name_get_change,
298   FALSE, FALSE, TRUE
299 };
300
301 /* REGION */
302
303 static void
304 gtk_css_selector_region_print (const GtkCssSelector *selector,
305                                GString              *string)
306 {
307   g_string_append (string, selector->data);
308 }
309
310 static gboolean
311 gtk_css_selector_region_match (const GtkCssSelector *selector,
312                                const GtkCssMatcher  *matcher)
313 {
314   const GtkCssSelector *previous;
315
316   if (!_gtk_css_matcher_has_region (matcher, selector->data, 0))
317     return FALSE;
318
319   previous = gtk_css_selector_previous (selector);
320   if (previous && previous->class == &GTK_CSS_SELECTOR_DESCENDANT &&
321       gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
322     return TRUE;
323
324   return gtk_css_selector_match (previous, matcher);
325 }
326
327 static GtkCssChange
328 gtk_css_selector_region_get_change (const GtkCssSelector *selector)
329 {
330   GtkCssChange change;
331
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);
335
336   return change;
337 }
338
339 static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
340   "region",
341   gtk_css_selector_region_print,
342   gtk_css_selector_region_match,
343   gtk_css_selector_region_get_change,
344   FALSE, FALSE, TRUE
345 };
346
347 /* CLASS */
348
349 static void
350 gtk_css_selector_class_print (const GtkCssSelector *selector,
351                               GString              *string)
352 {
353   g_string_append_c (string, '.');
354   g_string_append (string, selector->data);
355 }
356
357 static gboolean
358 gtk_css_selector_class_match (const GtkCssSelector *selector,
359                               const GtkCssMatcher  *matcher)
360 {
361   if (!_gtk_css_matcher_has_class (matcher, selector->data))
362     return FALSE;
363
364   return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
365 }
366
367 static GtkCssChange
368 gtk_css_selector_class_get_change (const GtkCssSelector *selector)
369 {
370   return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_CLASS;
371 }
372
373 static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
374   "class",
375   gtk_css_selector_class_print,
376   gtk_css_selector_class_match,
377   gtk_css_selector_class_get_change,
378   FALSE, TRUE, FALSE
379 };
380
381 /* ID */
382
383 static void
384 gtk_css_selector_id_print (const GtkCssSelector *selector,
385                            GString              *string)
386 {
387   g_string_append_c (string, '#');
388   g_string_append (string, selector->data);
389 }
390
391 static gboolean
392 gtk_css_selector_id_match (const GtkCssSelector *selector,
393                            const GtkCssMatcher  *matcher)
394 {
395   if (!_gtk_css_matcher_has_id (matcher, selector->data))
396     return FALSE;
397
398   return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
399 }
400
401 static GtkCssChange
402 gtk_css_selector_id_get_change (const GtkCssSelector *selector)
403 {
404   return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_ID;
405 }
406
407 static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
408   "id",
409   gtk_css_selector_id_print,
410   gtk_css_selector_id_match,
411   gtk_css_selector_id_get_change,
412   TRUE, FALSE, FALSE
413 };
414
415 /* PSEUDOCLASS FOR STATE */
416
417 static void
418 gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
419                                           GString              *string)
420 {
421   static const char * state_names[] = {
422     "active",
423     "hover",
424     "selected",
425     "insensitive",
426     "inconsistent",
427     "focus",
428     "backdrop"
429   };
430   guint i, state;
431
432   state = GPOINTER_TO_UINT (selector->data);
433   g_string_append_c (string, ':');
434
435   for (i = 0; i < G_N_ELEMENTS (state_names); i++)
436     {
437       if (state == (1 << i))
438         {
439           g_string_append (string, state_names[i]);
440           return;
441         }
442     }
443
444   g_assert_not_reached ();
445 }
446
447 static gboolean
448 gtk_css_selector_pseudoclass_state_match (const GtkCssSelector *selector,
449                                           const GtkCssMatcher  *matcher)
450 {
451   GtkStateFlags state = GPOINTER_TO_UINT (selector->data);
452
453   if ((_gtk_css_matcher_get_state (matcher) & state) != state)
454     return FALSE;
455
456   return gtk_css_selector_match (gtk_css_selector_previous (selector), matcher);
457 }
458
459 static GtkCssChange
460 gtk_css_selector_pseudoclass_state_get_change (const GtkCssSelector *selector)
461 {
462   return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_STATE;
463 }
464
465 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
466   "pseudoclass-state",
467   gtk_css_selector_pseudoclass_state_print,
468   gtk_css_selector_pseudoclass_state_match,
469   gtk_css_selector_pseudoclass_state_get_change,
470   FALSE, TRUE, FALSE
471 };
472
473 /* PSEUDOCLASS FOR POSITION */
474
475 static void
476 gtk_css_selector_pseudoclass_position_print (const GtkCssSelector *selector,
477                                              GString              *string)
478 {
479   static const char * flag_names[] = {
480     "nth-child(even)",
481     "nth-child(odd)",
482     "first-child",
483     "last-child",
484     "only-child",
485     "sorted"
486   };
487   guint i, state;
488
489   state = GPOINTER_TO_UINT (selector->data);
490   g_string_append_c (string, ':');
491
492   for (i = 0; i < G_N_ELEMENTS (flag_names); i++)
493     {
494       if (state == (1 << i))
495         {
496           g_string_append (string, flag_names[i]);
497           return;
498         }
499     }
500
501   g_assert_not_reached ();
502 }
503
504 static gboolean
505 gtk_css_selector_pseudoclass_position_match_for_region (const GtkCssSelector *selector,
506                                                         const GtkCssMatcher  *matcher)
507 {
508   GtkRegionFlags selector_flags;
509   const GtkCssSelector *previous;
510   
511   selector_flags = GPOINTER_TO_UINT (selector->data);
512   selector = gtk_css_selector_previous (selector);
513
514   if (!_gtk_css_matcher_has_region (matcher, selector->data, selector_flags))
515     return FALSE;
516
517   previous = gtk_css_selector_previous (selector);
518   if (previous && previous->class == &GTK_CSS_SELECTOR_DESCENDANT &&
519       gtk_css_selector_match (gtk_css_selector_previous (previous), matcher))
520     return TRUE;
521
522   return gtk_css_selector_match (previous, matcher);
523 }
524
525 static gboolean
526 gtk_css_selector_pseudoclass_position_match (const GtkCssSelector *selector,
527                                              const GtkCssMatcher  *matcher)
528 {
529   GtkRegionFlags region;
530   guint sibling, n_siblings;
531   const GtkCssSelector *previous;
532
533   previous = gtk_css_selector_previous (selector);
534   if (previous && previous->class == &GTK_CSS_SELECTOR_REGION)
535     return gtk_css_selector_pseudoclass_position_match_for_region (selector, matcher);
536
537   n_siblings = _gtk_css_matcher_get_n_siblings (matcher);
538   if (n_siblings == 0)
539     return FALSE;
540   sibling = _gtk_css_matcher_get_sibling_index (matcher);
541
542   region = GPOINTER_TO_UINT (selector->data);
543
544   switch (region)
545     {
546     case GTK_REGION_EVEN:
547       if (!(sibling % 2))
548         return FALSE;
549       break;
550     case GTK_REGION_ODD:
551       if (sibling % 2)
552         return FALSE;
553       break;
554     case GTK_REGION_FIRST:
555       if (sibling != 0)
556         return FALSE;
557       break;
558     case GTK_REGION_LAST:
559       if (sibling + 1 != n_siblings)
560         return FALSE;
561       break;
562     case GTK_REGION_ONLY:
563       if (n_siblings != 1)
564         return FALSE;
565       break;
566     case GTK_REGION_SORTED:
567       return FALSE;
568     default:
569       g_assert_not_reached ();
570       return FALSE;
571     }
572
573   return gtk_css_selector_match (previous, matcher);
574 }
575
576 static GtkCssChange
577 gtk_css_selector_pseudoclass_position_get_change (const GtkCssSelector *selector)
578 {
579   return gtk_css_selector_get_change (gtk_css_selector_previous (selector)) | GTK_CSS_CHANGE_POSITION;
580 }
581
582 static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
583   "pseudoclass-position",
584   gtk_css_selector_pseudoclass_position_print,
585   gtk_css_selector_pseudoclass_position_match,
586   gtk_css_selector_pseudoclass_position_get_change,
587   FALSE, TRUE, FALSE
588 };
589
590 /* API */
591
592 static guint
593 gtk_css_selector_size (const GtkCssSelector *selector)
594 {
595   guint size = 0;
596
597   while (selector)
598     {
599       selector = gtk_css_selector_previous (selector);
600       size++;
601     }
602
603   return size;
604 }
605
606 static GtkCssSelector *
607 gtk_css_selector_new (const GtkCssSelectorClass *class,
608                       GtkCssSelector            *selector,
609                       gconstpointer              data)
610 {
611   guint size;
612
613   size = gtk_css_selector_size (selector);
614   selector = g_realloc (selector, sizeof (GtkCssSelector) * (size + 1) + sizeof (gpointer));
615   if (size == 0)
616     selector[1].class = NULL;
617   else
618     memmove (selector + 1, selector, sizeof (GtkCssSelector) * size + sizeof (gpointer));
619
620   selector->class = class;
621   selector->data = data;
622
623   return selector;
624 }
625
626 static GtkCssSelector *
627 parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector)
628 {
629   char *name;
630     
631   name = _gtk_css_parser_try_name (parser, FALSE);
632
633   if (name == NULL)
634     {
635       _gtk_css_parser_error (parser, "Expected a valid name for class");
636       if (selector)
637         _gtk_css_selector_free (selector);
638       return NULL;
639     }
640
641   selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_CLASS,
642                                    selector,
643                                    g_intern_string (name));
644
645   g_free (name);
646
647   return selector;
648 }
649
650 static GtkCssSelector *
651 parse_selector_id (GtkCssParser *parser, GtkCssSelector *selector)
652 {
653   char *name;
654     
655   name = _gtk_css_parser_try_name (parser, FALSE);
656
657   if (name == NULL)
658     {
659       _gtk_css_parser_error (parser, "Expected a valid name for id");
660       if (selector)
661         _gtk_css_selector_free (selector);
662       return NULL;
663     }
664
665   selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ID,
666                                    selector,
667                                    g_intern_string (name));
668
669   g_free (name);
670
671   return selector;
672 }
673
674 static GtkCssSelector *
675 parse_selector_pseudo_class (GtkCssParser   *parser,
676                              GtkCssSelector *selector)
677 {
678   struct {
679     const char *name;
680     GtkRegionFlags region_flag;
681     GtkStateFlags state_flag;
682   } pseudo_classes[] = {
683     { "first-child",  GTK_REGION_FIRST, 0 },
684     { "last-child",   GTK_REGION_LAST, 0 },
685     { "only-child",   GTK_REGION_ONLY, 0 },
686     { "sorted",       GTK_REGION_SORTED, 0 },
687     { "active",       0, GTK_STATE_FLAG_ACTIVE },
688     { "prelight",     0, GTK_STATE_FLAG_PRELIGHT },
689     { "hover",        0, GTK_STATE_FLAG_PRELIGHT },
690     { "selected",     0, GTK_STATE_FLAG_SELECTED },
691     { "insensitive",  0, GTK_STATE_FLAG_INSENSITIVE },
692     { "inconsistent", 0, GTK_STATE_FLAG_INCONSISTENT },
693     { "focused",      0, GTK_STATE_FLAG_FOCUSED },
694     { "focus",        0, GTK_STATE_FLAG_FOCUSED },
695     { "backdrop",     0, GTK_STATE_FLAG_BACKDROP },
696     { NULL, }
697   }, nth_child_classes[] = {
698     { "first",        GTK_REGION_FIRST, 0 },
699     { "last",         GTK_REGION_LAST, 0 },
700     { "even",         GTK_REGION_EVEN, 0 },
701     { "odd",          GTK_REGION_ODD, 0 },
702     { NULL, }
703   }, *classes;
704   guint i;
705   char *name;
706   GError *error;
707
708   name = _gtk_css_parser_try_ident (parser, FALSE);
709   if (name == NULL)
710     {
711       _gtk_css_parser_error (parser, "Missing name of pseudo-class");
712       if (selector)
713         _gtk_css_selector_free (selector);
714       return NULL;
715     }
716
717   if (_gtk_css_parser_try (parser, "(", TRUE))
718     {
719       char *function = name;
720
721       name = _gtk_css_parser_try_ident (parser, TRUE);
722       if (!_gtk_css_parser_try (parser, ")", FALSE))
723         {
724           _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class");
725           if (selector)
726             _gtk_css_selector_free (selector);
727           return NULL;
728         }
729
730       if (g_ascii_strcasecmp (function, "nth-child") != 0)
731         {
732           error = g_error_new (GTK_CSS_PROVIDER_ERROR,
733                                GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
734                                "Unknown pseudo-class '%s(%s)'", function, name ? name : "");
735           _gtk_css_parser_take_error (parser, error);
736           g_free (function);
737           g_free (name);
738           if (selector)
739             _gtk_css_selector_free (selector);
740           return NULL;
741         }
742       
743       g_free (function);
744     
745       if (name == NULL)
746         {
747           error = g_error_new (GTK_CSS_PROVIDER_ERROR,
748                                GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
749                                "Unknown pseudo-class 'nth-child(%s)'", name);
750           _gtk_css_parser_take_error (parser, error);
751           if (selector)
752             _gtk_css_selector_free (selector);
753           return NULL;
754         }
755
756       classes = nth_child_classes;
757     }
758   else
759     classes = pseudo_classes;
760
761   for (i = 0; classes[i].name != NULL; i++)
762     {
763       if (g_ascii_strcasecmp (name, classes[i].name) == 0)
764         {
765           g_free (name);
766
767           if (classes[i].region_flag)
768             selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
769                                              selector,
770                                              GUINT_TO_POINTER (classes[i].region_flag));
771           else
772             selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
773                                              selector,
774                                              GUINT_TO_POINTER (classes[i].state_flag));
775
776           return selector;
777         }
778     }
779
780   if (classes == nth_child_classes)
781     error = g_error_new (GTK_CSS_PROVIDER_ERROR,
782                          GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
783                          "Unknown pseudo-class 'nth-child(%s)'", name);
784   else
785     error = g_error_new (GTK_CSS_PROVIDER_ERROR,
786                          GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
787                          "Unknown pseudo-class '%s'", name);
788
789   _gtk_css_parser_take_error (parser, error);
790   g_free (name);
791   if (selector)
792     _gtk_css_selector_free (selector);
793
794   return NULL;
795 }
796
797 static GtkCssSelector *
798 try_parse_name (GtkCssParser   *parser,
799                 GtkCssSelector *selector)
800 {
801   char *name;
802
803   name = _gtk_css_parser_try_ident (parser, FALSE);
804   if (name)
805     {
806       selector = gtk_css_selector_new (_gtk_style_context_check_region_name (name)
807                                        ? &GTK_CSS_SELECTOR_REGION
808                                        : &GTK_CSS_SELECTOR_NAME,
809                                        selector,
810                                        g_intern_string (name));
811       g_free (name);
812     }
813   else if (_gtk_css_parser_try (parser, "*", FALSE))
814     selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ANY, selector, NULL);
815   
816   return selector;
817 }
818
819 static GtkCssSelector *
820 parse_simple_selector (GtkCssParser   *parser,
821                        GtkCssSelector *selector)
822 {
823   guint size = gtk_css_selector_size (selector);
824   
825   selector = try_parse_name (parser, selector);
826
827   do {
828       if (_gtk_css_parser_try (parser, "#", FALSE))
829         selector = parse_selector_id (parser, selector);
830       else if (_gtk_css_parser_try (parser, ".", FALSE))
831         selector = parse_selector_class (parser, selector);
832       else if (_gtk_css_parser_try (parser, ":", FALSE))
833         selector = parse_selector_pseudo_class (parser, selector);
834       else if (gtk_css_selector_size (selector) == size)
835         {
836           _gtk_css_parser_error (parser, "Expected a valid selector");
837           if (selector)
838             _gtk_css_selector_free (selector);
839           return NULL;
840         }
841       else
842         break;
843     }
844   while (selector && !_gtk_css_parser_is_eof (parser));
845
846   _gtk_css_parser_skip_whitespace (parser);
847
848   return selector;
849 }
850
851 GtkCssSelector *
852 _gtk_css_selector_parse (GtkCssParser *parser)
853 {
854   GtkCssSelector *selector = NULL;
855
856   while ((selector = parse_simple_selector (parser, selector)) &&
857          !_gtk_css_parser_is_eof (parser) &&
858          !_gtk_css_parser_begins_with (parser, ',') &&
859          !_gtk_css_parser_begins_with (parser, '{'))
860     {
861       if (_gtk_css_parser_try (parser, "+", TRUE))
862         selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_ADJACENT, selector, NULL);
863       else if (_gtk_css_parser_try (parser, "~", TRUE))
864         selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_SIBLING, selector, NULL);
865       else if (_gtk_css_parser_try (parser, ">", TRUE))
866         selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_CHILD, selector, NULL);
867       else
868         selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_DESCENDANT, selector, NULL);
869     }
870
871   return selector;
872 }
873
874 void
875 _gtk_css_selector_free (GtkCssSelector *selector)
876 {
877   g_return_if_fail (selector != NULL);
878
879   g_free (selector);
880 }
881
882 void
883 _gtk_css_selector_print (const GtkCssSelector *selector,
884                          GString *             str)
885 {
886   const GtkCssSelector *previous;
887
888   g_return_if_fail (selector != NULL);
889
890   previous = gtk_css_selector_previous (selector);
891   if (previous)
892     _gtk_css_selector_print (previous, str);
893
894   selector->class->print (selector, str);
895 }
896
897 char *
898 _gtk_css_selector_to_string (const GtkCssSelector *selector)
899 {
900   GString *string;
901
902   g_return_val_if_fail (selector != NULL, NULL);
903
904   string = g_string_new (NULL);
905
906   _gtk_css_selector_print (selector, string);
907
908   return g_string_free (string, FALSE);
909 }
910
911 GtkCssChange
912 _gtk_css_selector_get_change (const GtkCssSelector *selector)
913 {
914   g_return_val_if_fail (selector != NULL, 0);
915
916   return gtk_css_selector_get_change (selector);
917 }
918
919 /**
920  * _gtk_css_selector_matches:
921  * @selector: the selector
922  * @path: the path to check
923  * @state: The state to match
924  *
925  * Checks if the @selector matches the given @path. If @length is
926  * smaller than the number of elements in @path, it is assumed that
927  * only the first @length element of @path are valid and the rest
928  * does not exist. This is useful for doing parent matches for the
929  * 'inherit' keyword.
930  *
931  * Returns: %TRUE if the selector matches @path
932  **/
933 gboolean
934 _gtk_css_selector_matches (const GtkCssSelector *selector,
935                            const GtkCssMatcher  *matcher)
936 {
937
938   g_return_val_if_fail (selector != NULL, FALSE);
939   g_return_val_if_fail (matcher != NULL, FALSE);
940
941   return gtk_css_selector_match (selector, matcher);
942 }
943
944 /* Computes specificity according to CSS 2.1.
945  * The arguments must be initialized to 0 */
946 static void
947 _gtk_css_selector_get_specificity (const GtkCssSelector *selector,
948                                    guint                *ids,
949                                    guint                *classes,
950                                    guint                *elements)
951 {
952   for (; selector; selector = gtk_css_selector_previous (selector))
953     {
954       const GtkCssSelectorClass *klass = selector->class;
955
956       if (klass->increase_id_specificity)
957         (*ids)++;
958       if (klass->increase_class_specificity)
959         (*classes)++;
960       if (klass->increase_element_specificity)
961         (*elements)++;
962     }
963 }
964
965 int
966 _gtk_css_selector_compare (const GtkCssSelector *a,
967                            const GtkCssSelector *b)
968 {
969   guint a_ids = 0, a_classes = 0, a_elements = 0;
970   guint b_ids = 0, b_classes = 0, b_elements = 0;
971   int compare;
972
973   _gtk_css_selector_get_specificity (a, &a_ids, &a_classes, &a_elements);
974   _gtk_css_selector_get_specificity (b, &b_ids, &b_classes, &b_elements);
975
976   compare = a_ids - b_ids;
977   if (compare)
978     return compare;
979
980   compare = a_classes - b_classes;
981   if (compare)
982     return compare;
983
984   return a_elements - b_elements;
985 }
986
987 GtkStateFlags
988 _gtk_css_selector_get_state_flags (const GtkCssSelector *selector)
989 {
990   GtkStateFlags state = 0;
991
992   g_return_val_if_fail (selector != NULL, 0);
993
994   for (; selector && selector->class == &GTK_CSS_SELECTOR_NAME; selector = gtk_css_selector_previous (selector))
995     state |= GPOINTER_TO_UINT (selector->data);
996
997   return state;
998 }
999