]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssmatcher.c
68207387fef8f729726b16707001c8f854e4a989
[~andy/gtk] / gtk / gtkcssmatcher.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2012 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 "gtkcssmatcherprivate.h"
21
22 #include "gtkwidgetpath.h"
23
24 /* GTK_CSS_MATCHER_WIDGET_PATH */
25
26 static gboolean
27 gtk_css_matcher_widget_path_get_parent (GtkCssMatcher       *matcher,
28                                         const GtkCssMatcher *child)
29 {
30   if (child->path.index == 0)
31     return FALSE;
32
33   matcher->path.klass = child->path.klass;
34   matcher->path.path = child->path.path;
35   matcher->path.state_flags = 0;
36   matcher->path.index = child->path.index - 1;
37   matcher->path.sibling_index = gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index);
38
39   return TRUE;
40 }
41
42 static gboolean
43 gtk_css_matcher_widget_path_get_previous (GtkCssMatcher       *matcher,
44                                           const GtkCssMatcher *next)
45 {
46   if (next->path.sibling_index == 0)
47     return FALSE;
48
49   matcher->path.klass = next->path.klass;
50   matcher->path.path = next->path.path;
51   matcher->path.state_flags = 0;
52   matcher->path.index = next->path.index;
53   matcher->path.sibling_index = next->path.sibling_index - 1;
54
55   return TRUE;
56 }
57
58 static GtkStateFlags
59 gtk_css_matcher_widget_path_get_state (const GtkCssMatcher *matcher)
60 {
61   return matcher->path.state_flags;
62 }
63
64 static gboolean
65 gtk_css_matcher_widget_path_has_name (const GtkCssMatcher *matcher,
66                                       const char          *name)
67 {
68   const GtkWidgetPath *siblings;
69   GType type;
70   
71   type = g_type_from_name (name);
72   siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
73   if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
74     return g_type_is_a (gtk_widget_path_iter_get_object_type (siblings, matcher->path.sibling_index), type);
75   else
76     return g_type_is_a (gtk_widget_path_iter_get_object_type (matcher->path.path, matcher->path.index), type);
77 }
78
79 static gboolean
80 gtk_css_matcher_widget_path_has_class (const GtkCssMatcher *matcher,
81                                        GQuark               class_name)
82 {
83   const GtkWidgetPath *siblings;
84   
85   siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
86   if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
87     return gtk_widget_path_iter_has_qclass (siblings, matcher->path.sibling_index, class_name);
88   else
89     return gtk_widget_path_iter_has_qclass (matcher->path.path, matcher->path.index, class_name);
90 }
91
92 static gboolean
93 gtk_css_matcher_widget_path_has_id (const GtkCssMatcher *matcher,
94                                     const char          *id)
95 {
96   const GtkWidgetPath *siblings;
97   
98   siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
99   if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
100     return gtk_widget_path_iter_has_name (siblings, matcher->path.sibling_index, id);
101   else
102     return gtk_widget_path_iter_has_name (matcher->path.path, matcher->path.index, id);
103 }
104
105 static gboolean
106 gtk_css_matcher_widget_path_has_regions (const GtkCssMatcher *matcher)
107 {
108   const GtkWidgetPath *siblings;
109   GSList *regions;
110   gboolean result;
111   
112   siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
113   if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
114     regions = gtk_widget_path_iter_list_regions (siblings, matcher->path.sibling_index);
115   else
116     regions = gtk_widget_path_iter_list_regions (matcher->path.path, matcher->path.index);
117   result = regions != NULL;
118   g_slist_free (regions);
119
120   return result;
121 }
122
123 static gboolean
124 gtk_css_matcher_widget_path_has_region (const GtkCssMatcher *matcher,
125                                         const char          *region,
126                                         GtkRegionFlags       flags)
127 {
128   const GtkWidgetPath *siblings;
129   GtkRegionFlags region_flags;
130   
131   siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
132   if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
133     {
134       if (!gtk_widget_path_iter_has_region (siblings, matcher->path.sibling_index, region, &region_flags))
135         return FALSE;
136     }
137   else
138     {
139       if (!gtk_widget_path_iter_has_region (matcher->path.path, matcher->path.index, region, &region_flags))
140         return FALSE;
141     }
142
143   if ((flags & region_flags) != flags)
144     return FALSE;
145
146   return TRUE;
147 }
148
149 static gboolean
150 gtk_css_matcher_widget_path_has_position (const GtkCssMatcher *matcher,
151                                           gboolean             forward,
152                                           int                  a,
153                                           int                  b)
154 {
155   const GtkWidgetPath *siblings;
156   int x;
157
158   siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
159   if (!siblings)
160     return FALSE;
161
162   if (forward)
163     x = matcher->path.sibling_index + 1;
164   else
165     x = gtk_widget_path_length (siblings) - matcher->path.sibling_index;
166
167   x -= b;
168
169   if (a == 0)
170     return x == 0;
171
172   if (x % a)
173     return FALSE;
174
175   return x / a > 0;
176 }
177
178 static const GtkCssMatcherClass GTK_CSS_MATCHER_WIDGET_PATH = {
179   gtk_css_matcher_widget_path_get_parent,
180   gtk_css_matcher_widget_path_get_previous,
181   gtk_css_matcher_widget_path_get_state,
182   gtk_css_matcher_widget_path_has_name,
183   gtk_css_matcher_widget_path_has_class,
184   gtk_css_matcher_widget_path_has_id,
185   gtk_css_matcher_widget_path_has_regions,
186   gtk_css_matcher_widget_path_has_region,
187   gtk_css_matcher_widget_path_has_position,
188   FALSE
189 };
190
191 gboolean
192 _gtk_css_matcher_init (GtkCssMatcher       *matcher,
193                        const GtkWidgetPath *path,
194                        GtkStateFlags        state)
195 {
196   if (gtk_widget_path_length (path) == 0)
197     return FALSE;
198
199   matcher->path.klass = &GTK_CSS_MATCHER_WIDGET_PATH;
200   matcher->path.path = path;
201   matcher->path.state_flags = state;
202   matcher->path.index = gtk_widget_path_length (path) - 1;
203   matcher->path.sibling_index = gtk_widget_path_iter_get_sibling_index (path, matcher->path.index);
204
205   return TRUE;
206 }
207
208 /* GTK_CSS_MATCHER_WIDGET_ANY */
209
210 static gboolean
211 gtk_css_matcher_any_get_parent (GtkCssMatcher       *matcher,
212                                 const GtkCssMatcher *child)
213 {
214   _gtk_css_matcher_any_init (matcher);
215
216   return TRUE;
217 }
218
219 static gboolean
220 gtk_css_matcher_any_get_previous (GtkCssMatcher       *matcher,
221                                   const GtkCssMatcher *next)
222 {
223   _gtk_css_matcher_any_init (matcher);
224
225   return TRUE;
226 }
227
228 static GtkStateFlags
229 gtk_css_matcher_any_get_state (const GtkCssMatcher *matcher)
230 {
231   /* XXX: This gets tricky when we implement :not() */
232
233   return GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED
234     | GTK_STATE_FLAG_INSENSITIVE | GTK_STATE_FLAG_INCONSISTENT
235     | GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_BACKDROP;
236 }
237
238 static gboolean
239 gtk_css_matcher_any_has_name (const GtkCssMatcher *matcher,
240                               const char          *name)
241 {
242   return TRUE;
243 }
244
245 static gboolean
246 gtk_css_matcher_any_has_class (const GtkCssMatcher *matcher,
247                                GQuark               class_name)
248 {
249   return TRUE;
250 }
251
252 static gboolean
253 gtk_css_matcher_any_has_id (const GtkCssMatcher *matcher,
254                                     const char          *id)
255 {
256   return TRUE;
257 }
258
259 static gboolean
260 gtk_css_matcher_any_has_regions (const GtkCssMatcher *matcher)
261 {
262   return TRUE;
263 }
264
265 static gboolean
266 gtk_css_matcher_any_has_region (const GtkCssMatcher *matcher,
267                                 const char          *region,
268                                 GtkRegionFlags       flags)
269 {
270   return TRUE;
271 }
272
273 static gboolean
274 gtk_css_matcher_any_has_position (const GtkCssMatcher *matcher,
275                                   gboolean             forward,
276                                   int                  a,
277                                   int                  b)
278 {
279   return TRUE;
280 }
281
282 static const GtkCssMatcherClass GTK_CSS_MATCHER_ANY = {
283   gtk_css_matcher_any_get_parent,
284   gtk_css_matcher_any_get_previous,
285   gtk_css_matcher_any_get_state,
286   gtk_css_matcher_any_has_name,
287   gtk_css_matcher_any_has_class,
288   gtk_css_matcher_any_has_id,
289   gtk_css_matcher_any_has_regions,
290   gtk_css_matcher_any_has_region,
291   gtk_css_matcher_any_has_position,
292   TRUE
293 };
294
295 void
296 _gtk_css_matcher_any_init (GtkCssMatcher *matcher)
297 {
298   matcher->klass = &GTK_CSS_MATCHER_ANY;
299 }
300
301 /* GTK_CSS_MATCHER_WIDGET_SUPERSET */
302
303 static gboolean
304 gtk_css_matcher_superset_get_parent (GtkCssMatcher       *matcher,
305                                      const GtkCssMatcher *child)
306 {
307   _gtk_css_matcher_any_init (matcher);
308
309   return TRUE;
310 }
311
312 static gboolean
313 gtk_css_matcher_superset_get_previous (GtkCssMatcher       *matcher,
314                                        const GtkCssMatcher *next)
315 {
316   _gtk_css_matcher_any_init (matcher);
317
318   return TRUE;
319 }
320
321 static GtkStateFlags
322 gtk_css_matcher_superset_get_state (const GtkCssMatcher *matcher)
323 {
324   /* XXX: This gets tricky when we implement :not() */
325
326   if (matcher->superset.relevant & GTK_CSS_CHANGE_STATE)
327     return _gtk_css_matcher_get_state (matcher->superset.subset);
328   else
329     return GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED
330       | GTK_STATE_FLAG_INSENSITIVE | GTK_STATE_FLAG_INCONSISTENT
331       | GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_BACKDROP;
332 }
333
334 static gboolean
335 gtk_css_matcher_superset_has_name (const GtkCssMatcher *matcher,
336                                    const char          *name)
337 {
338   if (matcher->superset.relevant & GTK_CSS_CHANGE_NAME)
339     return _gtk_css_matcher_has_name (matcher->superset.subset, name);
340   else
341     return TRUE;
342 }
343
344 static gboolean
345 gtk_css_matcher_superset_has_class (const GtkCssMatcher *matcher,
346                                     GQuark               class_name)
347 {
348   if (matcher->superset.relevant & GTK_CSS_CHANGE_CLASS)
349     return _gtk_css_matcher_has_class (matcher->superset.subset, class_name);
350   else
351     return TRUE;
352 }
353
354 static gboolean
355 gtk_css_matcher_superset_has_id (const GtkCssMatcher *matcher,
356                                  const char          *id)
357 {
358   if (matcher->superset.relevant & GTK_CSS_CHANGE_NAME)
359     return _gtk_css_matcher_has_id (matcher->superset.subset, id);
360   else
361     return TRUE;
362 }
363
364 static gboolean
365 gtk_css_matcher_superset_has_regions (const GtkCssMatcher *matcher)
366 {
367   if (matcher->superset.relevant & GTK_CSS_CHANGE_NAME)
368     return _gtk_css_matcher_has_regions (matcher->superset.subset);
369   else
370     return TRUE;
371 }
372
373 static gboolean
374 gtk_css_matcher_superset_has_region (const GtkCssMatcher *matcher,
375                                      const char          *region,
376                                      GtkRegionFlags       flags)
377 {
378   if (matcher->superset.relevant & GTK_CSS_CHANGE_NAME)
379     {
380       if (matcher->superset.relevant & GTK_CSS_CHANGE_POSITION)
381         return _gtk_css_matcher_has_region (matcher->superset.subset, region, flags);
382       else
383         return _gtk_css_matcher_has_region (matcher->superset.subset, region, 0);
384     }
385   else
386     return TRUE;
387 }
388
389 static gboolean
390 gtk_css_matcher_superset_has_position (const GtkCssMatcher *matcher,
391                                        gboolean             forward,
392                                        int                  a,
393                                        int                  b)
394 {
395   if (matcher->superset.relevant & GTK_CSS_CHANGE_POSITION)
396     return _gtk_css_matcher_has_position (matcher->superset.subset, forward, a, b);
397   else
398     return TRUE;
399 }
400
401 static const GtkCssMatcherClass GTK_CSS_MATCHER_SUPERSET = {
402   gtk_css_matcher_superset_get_parent,
403   gtk_css_matcher_superset_get_previous,
404   gtk_css_matcher_superset_get_state,
405   gtk_css_matcher_superset_has_name,
406   gtk_css_matcher_superset_has_class,
407   gtk_css_matcher_superset_has_id,
408   gtk_css_matcher_superset_has_regions,
409   gtk_css_matcher_superset_has_region,
410   gtk_css_matcher_superset_has_position,
411   FALSE
412 };
413
414 void
415 _gtk_css_matcher_superset_init (GtkCssMatcher       *matcher,
416                                 const GtkCssMatcher *subset,
417                                 GtkCssChange         relevant)
418 {
419   g_return_if_fail (subset != NULL);
420   g_return_if_fail ((relevant & ~(GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_STATE)) == 0);
421
422   matcher->superset.klass = &GTK_CSS_MATCHER_SUPERSET;
423   matcher->superset.subset = subset;
424   matcher->superset.relevant = relevant;
425 }
426