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