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 "gtkcssnodedeclarationprivate.h"
23 #include "gtkcssnodeprivate.h"
24 #include "gtkwidgetpath.h"
25
26 /* GTK_CSS_MATCHER_WIDGET_PATH */
27
28 static gboolean
gtk_css_matcher_widget_path_get_parent(GtkCssMatcher * matcher,const GtkCssMatcher * child)29 gtk_css_matcher_widget_path_get_parent (GtkCssMatcher *matcher,
30 const GtkCssMatcher *child)
31 {
32 if (child->path.index == 0)
33 return FALSE;
34
35 matcher->path.klass = child->path.klass;
36 matcher->path.decl = NULL;
37 matcher->path.path = child->path.path;
38 matcher->path.index = child->path.index - 1;
39 matcher->path.sibling_index = gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index);
40
41 return TRUE;
42 }
43
44 static gboolean
gtk_css_matcher_widget_path_get_previous(GtkCssMatcher * matcher,const GtkCssMatcher * next)45 gtk_css_matcher_widget_path_get_previous (GtkCssMatcher *matcher,
46 const GtkCssMatcher *next)
47 {
48 if (next->path.sibling_index == 0)
49 return FALSE;
50
51 matcher->path.klass = next->path.klass;
52 matcher->path.decl = NULL;
53 matcher->path.path = next->path.path;
54 matcher->path.index = next->path.index;
55 matcher->path.sibling_index = next->path.sibling_index - 1;
56
57 return TRUE;
58 }
59
60 static GtkStateFlags
gtk_css_matcher_widget_path_get_state(const GtkCssMatcher * matcher)61 gtk_css_matcher_widget_path_get_state (const GtkCssMatcher *matcher)
62 {
63 const GtkWidgetPath *siblings;
64
65 if (matcher->path.decl)
66 return gtk_css_node_declaration_get_state (matcher->path.decl);
67
68 siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
69 if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
70 return gtk_widget_path_iter_get_state (siblings, matcher->path.sibling_index);
71 else
72 return gtk_widget_path_iter_get_state (matcher->path.path, matcher->path.index);
73 }
74
75 static gboolean
gtk_css_matcher_widget_path_has_name(const GtkCssMatcher * matcher,const char * name)76 gtk_css_matcher_widget_path_has_name (const GtkCssMatcher *matcher,
77 /*interned*/ const char *name)
78 {
79 const GtkWidgetPath *siblings;
80
81 siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
82 if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
83 {
84 const char *path_name = gtk_widget_path_iter_get_object_name (siblings, matcher->path.sibling_index);
85
86 if (path_name == NULL)
87 path_name = g_type_name (gtk_widget_path_iter_get_object_type (siblings, matcher->path.sibling_index));
88
89 return path_name == name;
90 }
91 else
92 {
93 const char *path_name = gtk_widget_path_iter_get_object_name (matcher->path.path, matcher->path.index);
94
95 if (path_name == NULL)
96 path_name = g_type_name (gtk_widget_path_iter_get_object_type (matcher->path.path, matcher->path.index));
97
98 return path_name == name;
99 }
100 }
101
102 static gboolean
gtk_css_matcher_widget_path_has_class(const GtkCssMatcher * matcher,GQuark class_name)103 gtk_css_matcher_widget_path_has_class (const GtkCssMatcher *matcher,
104 GQuark class_name)
105 {
106 const GtkWidgetPath *siblings;
107
108 if (matcher->path.decl &&
109 gtk_css_node_declaration_has_class (matcher->path.decl, class_name))
110 return TRUE;
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 return gtk_widget_path_iter_has_qclass (siblings, matcher->path.sibling_index, class_name);
115 else
116 return gtk_widget_path_iter_has_qclass (matcher->path.path, matcher->path.index, class_name);
117 }
118
119 static gboolean
gtk_css_matcher_widget_path_has_id(const GtkCssMatcher * matcher,const char * id)120 gtk_css_matcher_widget_path_has_id (const GtkCssMatcher *matcher,
121 const char *id)
122 {
123 const GtkWidgetPath *siblings;
124
125 siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
126 if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
127 return gtk_widget_path_iter_has_name (siblings, matcher->path.sibling_index, id);
128 else
129 return gtk_widget_path_iter_has_name (matcher->path.path, matcher->path.index, id);
130 }
131
132 static gboolean
gtk_css_matcher_widget_path_has_position(const GtkCssMatcher * matcher,gboolean forward,int a,int b)133 gtk_css_matcher_widget_path_has_position (const GtkCssMatcher *matcher,
134 gboolean forward,
135 int a,
136 int b)
137 {
138 const GtkWidgetPath *siblings;
139 int x;
140
141 siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
142 if (!siblings)
143 return FALSE;
144
145 if (forward)
146 x = matcher->path.sibling_index + 1;
147 else
148 x = gtk_widget_path_length (siblings) - matcher->path.sibling_index;
149
150 x -= b;
151
152 if (a == 0)
153 return x == 0;
154
155 if (x % a)
156 return FALSE;
157
158 return x / a >= 0;
159 }
160
161 static const GtkCssMatcherClass GTK_CSS_MATCHER_WIDGET_PATH = {
162 gtk_css_matcher_widget_path_get_parent,
163 gtk_css_matcher_widget_path_get_previous,
164 gtk_css_matcher_widget_path_get_state,
165 gtk_css_matcher_widget_path_has_name,
166 gtk_css_matcher_widget_path_has_class,
167 gtk_css_matcher_widget_path_has_id,
168 gtk_css_matcher_widget_path_has_position,
169 FALSE
170 };
171
172 gboolean
_gtk_css_matcher_init(GtkCssMatcher * matcher,const GtkWidgetPath * path,const GtkCssNodeDeclaration * decl)173 _gtk_css_matcher_init (GtkCssMatcher *matcher,
174 const GtkWidgetPath *path,
175 const GtkCssNodeDeclaration *decl)
176 {
177 if (gtk_widget_path_length (path) == 0)
178 return FALSE;
179
180 matcher->path.klass = >K_CSS_MATCHER_WIDGET_PATH;
181 matcher->path.decl = decl;
182 matcher->path.path = path;
183 matcher->path.index = gtk_widget_path_length (path) - 1;
184 matcher->path.sibling_index = gtk_widget_path_iter_get_sibling_index (path, matcher->path.index);
185
186 return TRUE;
187 }
188
189 /* GTK_CSS_MATCHER_NODE */
190
191 static gboolean
gtk_css_matcher_node_get_parent(GtkCssMatcher * matcher,const GtkCssMatcher * child)192 gtk_css_matcher_node_get_parent (GtkCssMatcher *matcher,
193 const GtkCssMatcher *child)
194 {
195 GtkCssNode *node;
196
197 node = gtk_css_node_get_parent (child->node.node);
198 if (node == NULL)
199 return FALSE;
200
201 return gtk_css_node_init_matcher (node, matcher);
202 }
203
204 static GtkCssNode *
get_previous_visible_sibling(GtkCssNode * node)205 get_previous_visible_sibling (GtkCssNode *node)
206 {
207 do {
208 node = gtk_css_node_get_previous_sibling (node);
209 } while (node && !gtk_css_node_get_visible (node));
210
211 return node;
212 }
213
214 static GtkCssNode *
get_next_visible_sibling(GtkCssNode * node)215 get_next_visible_sibling (GtkCssNode *node)
216 {
217 do {
218 node = gtk_css_node_get_next_sibling (node);
219 } while (node && !gtk_css_node_get_visible (node));
220
221 return node;
222 }
223
224 static gboolean
gtk_css_matcher_node_get_previous(GtkCssMatcher * matcher,const GtkCssMatcher * next)225 gtk_css_matcher_node_get_previous (GtkCssMatcher *matcher,
226 const GtkCssMatcher *next)
227 {
228 GtkCssNode *node;
229
230 node = get_previous_visible_sibling (next->node.node);
231 if (node == NULL)
232 return FALSE;
233
234 return gtk_css_node_init_matcher (node, matcher);
235 }
236
237 static GtkStateFlags
gtk_css_matcher_node_get_state(const GtkCssMatcher * matcher)238 gtk_css_matcher_node_get_state (const GtkCssMatcher *matcher)
239 {
240 return gtk_css_node_get_state (matcher->node.node);
241 }
242
243 static gboolean
gtk_css_matcher_node_has_name(const GtkCssMatcher * matcher,const char * name)244 gtk_css_matcher_node_has_name (const GtkCssMatcher *matcher,
245 /*interned*/ const char *name)
246 {
247 return gtk_css_node_get_name (matcher->node.node) == name;
248 }
249
250 static gboolean
gtk_css_matcher_node_has_class(const GtkCssMatcher * matcher,GQuark class_name)251 gtk_css_matcher_node_has_class (const GtkCssMatcher *matcher,
252 GQuark class_name)
253 {
254 return gtk_css_node_has_class (matcher->node.node, class_name);
255 }
256
257 static gboolean
gtk_css_matcher_node_has_id(const GtkCssMatcher * matcher,const char * id)258 gtk_css_matcher_node_has_id (const GtkCssMatcher *matcher,
259 const char *id)
260 {
261 /* assume all callers pass an interned string */
262 return gtk_css_node_get_id (matcher->node.node) == id;
263 }
264
265 static gboolean
gtk_css_matcher_node_nth_child(GtkCssNode * node,GtkCssNode * (* prev_node_func)(GtkCssNode *),int a,int b)266 gtk_css_matcher_node_nth_child (GtkCssNode *node,
267 GtkCssNode *(* prev_node_func) (GtkCssNode *),
268 int a,
269 int b)
270 {
271 int pos, x;
272
273 /* special-case the common "first-child" and "last-child" */
274 if (a == 0)
275 {
276 while (b > 0 && node != NULL)
277 {
278 b--;
279 node = prev_node_func (node);
280 }
281
282 return b == 0 && node == NULL;
283 }
284
285 /* count nodes */
286 for (pos = 0; node != NULL; pos++)
287 node = prev_node_func (node);
288
289 /* solve pos = a * X + b
290 * and return TRUE if X is integer >= 0 */
291 x = pos - b;
292
293 if (x % a)
294 return FALSE;
295
296 return x / a >= 0;
297 }
298
299 static gboolean
gtk_css_matcher_node_has_position(const GtkCssMatcher * matcher,gboolean forward,int a,int b)300 gtk_css_matcher_node_has_position (const GtkCssMatcher *matcher,
301 gboolean forward,
302 int a,
303 int b)
304 {
305 return gtk_css_matcher_node_nth_child (matcher->node.node,
306 forward ? get_previous_visible_sibling
307 : get_next_visible_sibling,
308 a, b);
309 }
310
311 static const GtkCssMatcherClass GTK_CSS_MATCHER_NODE = {
312 gtk_css_matcher_node_get_parent,
313 gtk_css_matcher_node_get_previous,
314 gtk_css_matcher_node_get_state,
315 gtk_css_matcher_node_has_name,
316 gtk_css_matcher_node_has_class,
317 gtk_css_matcher_node_has_id,
318 gtk_css_matcher_node_has_position,
319 FALSE
320 };
321
322 void
_gtk_css_matcher_node_init(GtkCssMatcher * matcher,GtkCssNode * node)323 _gtk_css_matcher_node_init (GtkCssMatcher *matcher,
324 GtkCssNode *node)
325 {
326 matcher->node.klass = >K_CSS_MATCHER_NODE;
327 matcher->node.node = node;
328 }
329
330 /* GTK_CSS_MATCHER_WIDGET_ANY */
331
332 static gboolean
gtk_css_matcher_any_get_parent(GtkCssMatcher * matcher,const GtkCssMatcher * child)333 gtk_css_matcher_any_get_parent (GtkCssMatcher *matcher,
334 const GtkCssMatcher *child)
335 {
336 _gtk_css_matcher_any_init (matcher);
337
338 return TRUE;
339 }
340
341 static gboolean
gtk_css_matcher_any_get_previous(GtkCssMatcher * matcher,const GtkCssMatcher * next)342 gtk_css_matcher_any_get_previous (GtkCssMatcher *matcher,
343 const GtkCssMatcher *next)
344 {
345 _gtk_css_matcher_any_init (matcher);
346
347 return TRUE;
348 }
349
350 static GtkStateFlags
gtk_css_matcher_any_get_state(const GtkCssMatcher * matcher)351 gtk_css_matcher_any_get_state (const GtkCssMatcher *matcher)
352 {
353 /* XXX: This gets tricky when we implement :not() */
354
355 return GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED
356 | GTK_STATE_FLAG_INSENSITIVE | GTK_STATE_FLAG_INCONSISTENT
357 | GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_BACKDROP | GTK_STATE_FLAG_LINK
358 | GTK_STATE_FLAG_VISITED;
359 }
360
361 static gboolean
gtk_css_matcher_any_has_name(const GtkCssMatcher * matcher,const char * name)362 gtk_css_matcher_any_has_name (const GtkCssMatcher *matcher,
363 /*interned*/ const char *name)
364 {
365 return TRUE;
366 }
367
368 static gboolean
gtk_css_matcher_any_has_class(const GtkCssMatcher * matcher,GQuark class_name)369 gtk_css_matcher_any_has_class (const GtkCssMatcher *matcher,
370 GQuark class_name)
371 {
372 return TRUE;
373 }
374
375 static gboolean
gtk_css_matcher_any_has_id(const GtkCssMatcher * matcher,const char * id)376 gtk_css_matcher_any_has_id (const GtkCssMatcher *matcher,
377 const char *id)
378 {
379 return TRUE;
380 }
381
382 static gboolean
gtk_css_matcher_any_has_position(const GtkCssMatcher * matcher,gboolean forward,int a,int b)383 gtk_css_matcher_any_has_position (const GtkCssMatcher *matcher,
384 gboolean forward,
385 int a,
386 int b)
387 {
388 return TRUE;
389 }
390
391 static const GtkCssMatcherClass GTK_CSS_MATCHER_ANY = {
392 gtk_css_matcher_any_get_parent,
393 gtk_css_matcher_any_get_previous,
394 gtk_css_matcher_any_get_state,
395 gtk_css_matcher_any_has_name,
396 gtk_css_matcher_any_has_class,
397 gtk_css_matcher_any_has_id,
398 gtk_css_matcher_any_has_position,
399 TRUE
400 };
401
402 void
_gtk_css_matcher_any_init(GtkCssMatcher * matcher)403 _gtk_css_matcher_any_init (GtkCssMatcher *matcher)
404 {
405 matcher->klass = >K_CSS_MATCHER_ANY;
406 }
407
408 /* GTK_CSS_MATCHER_WIDGET_SUPERSET */
409
410 static gboolean
gtk_css_matcher_superset_get_parent(GtkCssMatcher * matcher,const GtkCssMatcher * child)411 gtk_css_matcher_superset_get_parent (GtkCssMatcher *matcher,
412 const GtkCssMatcher *child)
413 {
414 _gtk_css_matcher_any_init (matcher);
415
416 return TRUE;
417 }
418
419 static gboolean
gtk_css_matcher_superset_get_previous(GtkCssMatcher * matcher,const GtkCssMatcher * next)420 gtk_css_matcher_superset_get_previous (GtkCssMatcher *matcher,
421 const GtkCssMatcher *next)
422 {
423 _gtk_css_matcher_any_init (matcher);
424
425 return TRUE;
426 }
427
428 static GtkStateFlags
gtk_css_matcher_superset_get_state(const GtkCssMatcher * matcher)429 gtk_css_matcher_superset_get_state (const GtkCssMatcher *matcher)
430 {
431 /* XXX: This gets tricky when we implement :not() */
432
433 if (matcher->superset.relevant & GTK_CSS_CHANGE_STATE)
434 return _gtk_css_matcher_get_state (matcher->superset.subset);
435 else
436 return GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED
437 | GTK_STATE_FLAG_INSENSITIVE | GTK_STATE_FLAG_INCONSISTENT
438 | GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_BACKDROP | GTK_STATE_FLAG_LINK
439 | GTK_STATE_FLAG_VISITED;
440 }
441
442 static gboolean
gtk_css_matcher_superset_has_name(const GtkCssMatcher * matcher,const char * name)443 gtk_css_matcher_superset_has_name (const GtkCssMatcher *matcher,
444 /*interned*/ const char *name)
445 {
446 if (matcher->superset.relevant & GTK_CSS_CHANGE_NAME)
447 return _gtk_css_matcher_has_name (matcher->superset.subset, name);
448 else
449 return TRUE;
450 }
451
452 static gboolean
gtk_css_matcher_superset_has_class(const GtkCssMatcher * matcher,GQuark class_name)453 gtk_css_matcher_superset_has_class (const GtkCssMatcher *matcher,
454 GQuark class_name)
455 {
456 if (matcher->superset.relevant & GTK_CSS_CHANGE_CLASS)
457 return _gtk_css_matcher_has_class (matcher->superset.subset, class_name);
458 else
459 return TRUE;
460 }
461
462 static gboolean
gtk_css_matcher_superset_has_id(const GtkCssMatcher * matcher,const char * id)463 gtk_css_matcher_superset_has_id (const GtkCssMatcher *matcher,
464 const char *id)
465 {
466 if (matcher->superset.relevant & GTK_CSS_CHANGE_NAME)
467 return _gtk_css_matcher_has_id (matcher->superset.subset, id);
468 else
469 return TRUE;
470 }
471
472 static gboolean
gtk_css_matcher_superset_has_position(const GtkCssMatcher * matcher,gboolean forward,int a,int b)473 gtk_css_matcher_superset_has_position (const GtkCssMatcher *matcher,
474 gboolean forward,
475 int a,
476 int b)
477 {
478 if (matcher->superset.relevant & GTK_CSS_CHANGE_POSITION)
479 return _gtk_css_matcher_has_position (matcher->superset.subset, forward, a, b);
480 else
481 return TRUE;
482 }
483
484 static const GtkCssMatcherClass GTK_CSS_MATCHER_SUPERSET = {
485 gtk_css_matcher_superset_get_parent,
486 gtk_css_matcher_superset_get_previous,
487 gtk_css_matcher_superset_get_state,
488 gtk_css_matcher_superset_has_name,
489 gtk_css_matcher_superset_has_class,
490 gtk_css_matcher_superset_has_id,
491 gtk_css_matcher_superset_has_position,
492 FALSE
493 };
494
495 void
_gtk_css_matcher_superset_init(GtkCssMatcher * matcher,const GtkCssMatcher * subset,GtkCssChange relevant)496 _gtk_css_matcher_superset_init (GtkCssMatcher *matcher,
497 const GtkCssMatcher *subset,
498 GtkCssChange relevant)
499 {
500 g_return_if_fail (subset != NULL);
501 g_return_if_fail ((relevant & ~(GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_STATE)) == 0);
502
503 matcher->superset.klass = >K_CSS_MATCHER_SUPERSET;
504 matcher->superset.subset = subset;
505 matcher->superset.relevant = relevant;
506 }
507
508