1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2014 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 "gtkcssnodeprivate.h"
21 
22 #include "gtkcssanimatedstyleprivate.h"
23 #include "gtkcsssectionprivate.h"
24 #include "gtkcssstylepropertyprivate.h"
25 #include "gtkintl.h"
26 #include "gtkmarshalers.h"
27 #include "gtksettingsprivate.h"
28 #include "gtktypebuiltins.h"
29 
30 /*
31  * CSS nodes are the backbone of the GtkStyleContext implementation and
32  * replace the role that GtkWidgetPath played in the past. A CSS node has
33  * an element name and a state, and can have an id and style classes, which
34  * is what is needed to determine the matching CSS selectors. CSS nodes have
35  * a 'visible' property, which makes it possible to temporarily 'hide' them
36  * from CSS matching - e.g. an invisible node will not affect :nth-child
37  * matching and so forth.
38  *
39  * The API to manage states, names, ids and classes of CSS nodes is:
40  * - gtk_css_node_get/set_state. States are represented as GtkStateFlags
41  * - gtk_css_node_get/set_name. Names are represented as interned strings
42  * - gtk_css_node_get/set_id. Ids are represented as interned strings
43  * - gtk_css_node_add/remove/has_class and gtk_css_node_list_classes. Style
44  *   classes are represented as quarks.
45  *
46  * CSS nodes are organized in a dom-like tree, and there is API to navigate
47  * and manipulate this tree:
48  * - gtk_css_node_set_parent
49  * - gtk_css_node_insert_before/after
50  * - gtk_css_node_get_parent
51  * - gtk_css_node_get_first/last_child
52  * - gtk_css_node_get_previous/next_sibling
53  * Note that parents keep a reference on their children in this tree.
54  *
55  * Every widget has one or more CSS nodes - the first one gets created
56  * automatically by GtkStyleContext. To set the name of the main node,
57  * call gtk_widget_class_set_css_name() in class_init(). Widget implementations
58  * can and should add subnodes as suitable.
59  *
60  * Best practice is:
61  * - For permanent subnodes, create them in init(), and keep a pointer
62  *   to the node (you don't have to keep a reference, cleanup will be
63  *   automatic by means of the parent node getting cleaned up by the
64  *   style context).
65  * - For transient nodes, create/destroy them when the conditions that
66  *   warrant their existence change.
67  * - Keep the state of all your nodes up-to-date. This probably requires
68  *   a ::state-flags-changed (and possibly ::direction-changed) handler,
69  *   as well as code to update the state in other places. Note that GTK+
70  *   does this automatically for the widget's main CSS node.
71  * - The sibling ordering in the CSS node tree is supposed to correspond
72  *   to the visible order of content: top-to-bottom and left-to-right.
73  *   Reorder your nodes to maintain this correlation. In particular for
74  *   horizontally layed out widgets, this will require listening to
75  *   ::direction-changed.
76  * - The draw function should just use gtk_style_context_save_to_node() to
77  *   'switch' to the right node, not make any other changes to the style
78  *   context.
79  *
80  * A noteworthy difference between gtk_style_context_save() and
81  * gtk_style_context_save_to_node() is that the former inherits all the
82  * style classes from the main CSS node, which often leads to unintended
83  * inheritance.
84  */
85 
86 /* When these change we do a full restyling. Otherwise we try to figure out
87  * if we need to change things. */
88 #define GTK_CSS_RADICAL_CHANGE (GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE | GTK_CSS_CHANGE_PARENT_STYLE)
89 
90 G_DEFINE_TYPE (GtkCssNode, gtk_css_node, G_TYPE_OBJECT)
91 
92 enum {
93   NODE_ADDED,
94   NODE_REMOVED,
95   STYLE_CHANGED,
96   LAST_SIGNAL
97 };
98 
99 enum {
100   PROP_0,
101   PROP_CLASSES,
102   PROP_ID,
103   PROP_NAME,
104   PROP_STATE,
105   PROP_VISIBLE,
106   PROP_WIDGET_TYPE,
107   NUM_PROPERTIES
108 };
109 
110 struct _GtkCssNodeStyleChange {
111   GtkCssStyle *old_style;
112   GtkCssStyle *new_style;
113 };
114 
115 static guint cssnode_signals[LAST_SIGNAL] = { 0 };
116 static GParamSpec *cssnode_properties[NUM_PROPERTIES];
117 
118 static GtkStyleProviderPrivate *
gtk_css_node_get_style_provider_or_null(GtkCssNode * cssnode)119 gtk_css_node_get_style_provider_or_null (GtkCssNode *cssnode)
120 {
121   return GTK_CSS_NODE_GET_CLASS (cssnode)->get_style_provider (cssnode);
122 }
123 
124 static void
gtk_css_node_set_invalid(GtkCssNode * node,gboolean invalid)125 gtk_css_node_set_invalid (GtkCssNode *node,
126                           gboolean    invalid)
127 {
128   if (node->invalid == invalid)
129     return;
130 
131   node->invalid = invalid;
132 
133   if (node->visible)
134     {
135       if (node->parent)
136         {
137           if (invalid)
138             gtk_css_node_set_invalid (node->parent, TRUE);
139         }
140       else
141         {
142           if (invalid)
143             GTK_CSS_NODE_GET_CLASS (node)->queue_validate (node);
144           else
145             GTK_CSS_NODE_GET_CLASS (node)->dequeue_validate (node);
146         }
147     }
148 }
149 
150 static void
gtk_css_node_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)151 gtk_css_node_get_property (GObject    *object,
152                            guint       property_id,
153                            GValue     *value,
154                            GParamSpec *pspec)
155 {
156   GtkCssNode *cssnode = GTK_CSS_NODE (object);
157 
158   switch (property_id)
159     {
160     case PROP_CLASSES:
161       g_value_take_boxed (value, gtk_css_node_get_classes (cssnode));
162       break;
163 
164     case PROP_ID:
165       g_value_set_string (value, gtk_css_node_get_id (cssnode));
166       break;
167 
168     case PROP_NAME:
169       g_value_set_string (value, gtk_css_node_get_name (cssnode));
170       break;
171 
172     case PROP_STATE:
173       g_value_set_flags (value, gtk_css_node_get_state (cssnode));
174       break;
175 
176     case PROP_VISIBLE:
177       g_value_set_boolean (value, gtk_css_node_get_visible (cssnode));
178       break;
179 
180     case PROP_WIDGET_TYPE:
181       g_value_set_gtype (value, gtk_css_node_get_widget_type (cssnode));
182       break;
183 
184     default:
185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
186     }
187 }
188 
189 static void
gtk_css_node_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)190 gtk_css_node_set_property (GObject      *object,
191                            guint         property_id,
192                            const GValue *value,
193                            GParamSpec   *pspec)
194 {
195   GtkCssNode *cssnode = GTK_CSS_NODE (object);
196 
197   switch (property_id)
198     {
199     case PROP_CLASSES:
200       gtk_css_node_set_classes (cssnode, g_value_get_boxed (value));
201       break;
202 
203     case PROP_ID:
204       gtk_css_node_set_id (cssnode, g_value_get_string (value));
205       break;
206 
207     case PROP_NAME:
208       gtk_css_node_set_name (cssnode, g_value_get_string (value));
209       break;
210 
211     case PROP_STATE:
212       gtk_css_node_set_state (cssnode, g_value_get_flags (value));
213       break;
214 
215     case PROP_VISIBLE:
216       gtk_css_node_set_visible (cssnode, g_value_get_boolean (value));
217       break;
218 
219     case PROP_WIDGET_TYPE:
220       gtk_css_node_set_widget_type (cssnode, g_value_get_gtype (value));
221       break;
222 
223     default:
224       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
225     }
226 }
227 
228 static void
gtk_css_node_dispose(GObject * object)229 gtk_css_node_dispose (GObject *object)
230 {
231   GtkCssNode *cssnode = GTK_CSS_NODE (object);
232 
233   while (cssnode->first_child)
234     {
235       gtk_css_node_set_parent (cssnode->first_child, NULL);
236     }
237 
238   gtk_css_node_set_invalid (cssnode, FALSE);
239 
240   g_clear_pointer (&cssnode->cache, gtk_css_node_style_cache_unref);
241 
242   G_OBJECT_CLASS (gtk_css_node_parent_class)->dispose (object);
243 }
244 
245 static void
gtk_css_node_finalize(GObject * object)246 gtk_css_node_finalize (GObject *object)
247 {
248   GtkCssNode *cssnode = GTK_CSS_NODE (object);
249 
250   if (cssnode->style)
251     g_object_unref (cssnode->style);
252   gtk_css_node_declaration_unref (cssnode->decl);
253 
254   G_OBJECT_CLASS (gtk_css_node_parent_class)->finalize (object);
255 }
256 
257 static gboolean
gtk_css_node_is_first_child(GtkCssNode * node)258 gtk_css_node_is_first_child (GtkCssNode *node)
259 {
260   GtkCssNode *iter;
261 
262   for (iter = node->previous_sibling;
263        iter != NULL;
264        iter = iter->previous_sibling)
265     {
266       if (iter->visible)
267         return FALSE;
268     }
269   return TRUE;
270 }
271 
272 static gboolean
gtk_css_node_is_last_child(GtkCssNode * node)273 gtk_css_node_is_last_child (GtkCssNode *node)
274 {
275   GtkCssNode *iter;
276 
277   for (iter = node->next_sibling;
278        iter != NULL;
279        iter = iter->next_sibling)
280     {
281       if (iter->visible)
282         return FALSE;
283     }
284   return TRUE;
285 }
286 
287 static gboolean
may_use_global_parent_cache(GtkCssNode * node)288 may_use_global_parent_cache (GtkCssNode *node)
289 {
290   GtkStyleProviderPrivate *provider;
291   GtkCssNode *parent;
292 
293   parent = gtk_css_node_get_parent (node);
294   if (parent == NULL)
295     return FALSE;
296 
297   provider = gtk_css_node_get_style_provider_or_null (node);
298   if (provider != NULL && provider != gtk_css_node_get_style_provider (parent))
299     return FALSE;
300 
301   return TRUE;
302 }
303 
304 static GtkCssStyle *
lookup_in_global_parent_cache(GtkCssNode * node,const GtkCssNodeDeclaration * decl)305 lookup_in_global_parent_cache (GtkCssNode                  *node,
306                                const GtkCssNodeDeclaration *decl)
307 {
308   GtkCssNode *parent;
309 
310   parent = node->parent;
311 
312   if (parent == NULL ||
313       !may_use_global_parent_cache (node))
314     return NULL;
315 
316   if (parent->cache == NULL)
317     return NULL;
318 
319   g_assert (node->cache == NULL);
320   node->cache = gtk_css_node_style_cache_lookup (parent->cache,
321                                                  decl,
322                                                  gtk_css_node_is_first_child (node),
323                                                  gtk_css_node_is_last_child (node));
324   if (node->cache == NULL)
325     return NULL;
326 
327   return gtk_css_node_style_cache_get_style (node->cache);
328 }
329 
330 static void
store_in_global_parent_cache(GtkCssNode * node,const GtkCssNodeDeclaration * decl,GtkCssStyle * style)331 store_in_global_parent_cache (GtkCssNode                  *node,
332                               const GtkCssNodeDeclaration *decl,
333                               GtkCssStyle                 *style)
334 {
335   GtkCssNode *parent;
336 
337   g_assert (GTK_IS_CSS_STATIC_STYLE (style));
338 
339   parent = node->parent;
340 
341   if (parent == NULL ||
342       !may_use_global_parent_cache (node))
343     return;
344 
345   if (parent->cache == NULL)
346     parent->cache = gtk_css_node_style_cache_new (parent->style);
347 
348   node->cache = gtk_css_node_style_cache_insert (parent->cache,
349                                                  (GtkCssNodeDeclaration *) decl,
350                                                  gtk_css_node_is_first_child (node),
351                                                  gtk_css_node_is_last_child (node),
352                                                  style);
353 }
354 
355 static GtkCssStyle *
gtk_css_node_create_style(GtkCssNode * cssnode)356 gtk_css_node_create_style (GtkCssNode *cssnode)
357 {
358   const GtkCssNodeDeclaration *decl;
359   GtkCssMatcher matcher;
360   GtkCssStyle *parent;
361   GtkCssStyle *style;
362 
363   decl = gtk_css_node_get_declaration (cssnode);
364   parent = cssnode->parent ? cssnode->parent->style : NULL;
365 
366   style = lookup_in_global_parent_cache (cssnode, decl);
367   if (style)
368     return g_object_ref (style);
369 
370   if (gtk_css_node_init_matcher (cssnode, &matcher))
371     style = gtk_css_static_style_new_compute (gtk_css_node_get_style_provider (cssnode),
372                                               &matcher,
373                                               parent);
374   else
375     style = gtk_css_static_style_new_compute (gtk_css_node_get_style_provider (cssnode),
376                                               NULL,
377                                               parent);
378 
379   store_in_global_parent_cache (cssnode, decl, style);
380 
381   return style;
382 }
383 
384 static gboolean
should_create_transitions(GtkCssChange change)385 should_create_transitions (GtkCssChange change)
386 {
387   return (change & GTK_CSS_CHANGE_ANIMATIONS) == 0;
388 }
389 
390 static gboolean
gtk_css_style_needs_recreation(GtkCssStyle * style,GtkCssChange change)391 gtk_css_style_needs_recreation (GtkCssStyle  *style,
392                                 GtkCssChange  change)
393 {
394   /* Try to avoid invalidating if we can */
395   if (change & GTK_CSS_RADICAL_CHANGE)
396     return TRUE;
397 
398   if (GTK_IS_CSS_ANIMATED_STYLE (style))
399     style = GTK_CSS_ANIMATED_STYLE (style)->style;
400 
401   if (gtk_css_static_style_get_change (GTK_CSS_STATIC_STYLE (style)) & change)
402     return TRUE;
403   else
404     return FALSE;
405 }
406 
407 static GtkCssStyle *
gtk_css_node_real_update_style(GtkCssNode * cssnode,GtkCssChange change,gint64 timestamp,GtkCssStyle * style)408 gtk_css_node_real_update_style (GtkCssNode   *cssnode,
409                                 GtkCssChange  change,
410                                 gint64        timestamp,
411                                 GtkCssStyle  *style)
412 {
413   GtkCssStyle *static_style, *new_static_style, *new_style;
414 
415   if (GTK_IS_CSS_ANIMATED_STYLE (style))
416     {
417       static_style = GTK_CSS_ANIMATED_STYLE (style)->style;
418     }
419   else
420     {
421       static_style = style;
422     }
423 
424   if (gtk_css_style_needs_recreation (static_style, change))
425     new_static_style = gtk_css_node_create_style (cssnode);
426   else
427     new_static_style = g_object_ref (static_style);
428 
429   if (new_static_style != static_style || (change & GTK_CSS_CHANGE_ANIMATIONS))
430     {
431       GtkCssNode *parent = gtk_css_node_get_parent (cssnode);
432       new_style = gtk_css_animated_style_new (new_static_style,
433                                               parent ? gtk_css_node_get_style (parent) : NULL,
434                                               timestamp,
435                                               gtk_css_node_get_style_provider (cssnode),
436                                               should_create_transitions (change) ? style : NULL);
437 
438       /* Clear the cache again, the static style we looked up above
439        * may have populated it. */
440       g_clear_pointer (&cssnode->cache, gtk_css_node_style_cache_unref);
441     }
442   else if (static_style != style && (change & GTK_CSS_CHANGE_TIMESTAMP))
443     {
444       new_style = gtk_css_animated_style_new_advance (GTK_CSS_ANIMATED_STYLE (style),
445                                                       static_style,
446                                                       timestamp);
447     }
448   else
449     {
450       new_style = g_object_ref (style);
451     }
452 
453   if (!gtk_css_style_is_static (new_style))
454     gtk_css_node_set_invalid (cssnode, TRUE);
455 
456   g_object_unref (new_static_style);
457 
458   return new_style;
459 }
460 
461 static void
gtk_css_node_real_invalidate(GtkCssNode * node)462 gtk_css_node_real_invalidate (GtkCssNode *node)
463 {
464 }
465 
466 static void
gtk_css_node_real_queue_validate(GtkCssNode * node)467 gtk_css_node_real_queue_validate (GtkCssNode *node)
468 {
469 }
470 
471 static void
gtk_css_node_real_dequeue_validate(GtkCssNode * node)472 gtk_css_node_real_dequeue_validate (GtkCssNode *node)
473 {
474 }
475 
476 static void
gtk_css_node_real_validate(GtkCssNode * node)477 gtk_css_node_real_validate (GtkCssNode *node)
478 {
479 }
480 
481 gboolean
gtk_css_node_real_init_matcher(GtkCssNode * cssnode,GtkCssMatcher * matcher)482 gtk_css_node_real_init_matcher (GtkCssNode     *cssnode,
483                                 GtkCssMatcher  *matcher)
484 {
485   _gtk_css_matcher_node_init (matcher, cssnode);
486 
487   return TRUE;
488 }
489 
490 static GtkWidgetPath *
gtk_css_node_real_create_widget_path(GtkCssNode * cssnode)491 gtk_css_node_real_create_widget_path (GtkCssNode *cssnode)
492 {
493   return gtk_widget_path_new ();
494 }
495 
496 static const GtkWidgetPath *
gtk_css_node_real_get_widget_path(GtkCssNode * cssnode)497 gtk_css_node_real_get_widget_path (GtkCssNode *cssnode)
498 {
499   return NULL;
500 }
501 
502 static GtkStyleProviderPrivate *
gtk_css_node_real_get_style_provider(GtkCssNode * cssnode)503 gtk_css_node_real_get_style_provider (GtkCssNode *cssnode)
504 {
505   return NULL;
506 }
507 
508 static GdkFrameClock *
gtk_css_node_real_get_frame_clock(GtkCssNode * cssnode)509 gtk_css_node_real_get_frame_clock (GtkCssNode *cssnode)
510 {
511   return NULL;
512 }
513 
514 static void
gtk_css_node_real_node_removed(GtkCssNode * parent,GtkCssNode * node,GtkCssNode * previous)515 gtk_css_node_real_node_removed (GtkCssNode *parent,
516                                 GtkCssNode *node,
517                                 GtkCssNode *previous)
518 {
519   if (node->previous_sibling)
520     node->previous_sibling->next_sibling = node->next_sibling;
521   else
522     node->parent->first_child = node->next_sibling;
523 
524   if (node->next_sibling)
525     node->next_sibling->previous_sibling = node->previous_sibling;
526   else
527     node->parent->last_child = node->previous_sibling;
528 
529   node->previous_sibling = NULL;
530   node->next_sibling = NULL;
531   node->parent = NULL;
532 }
533 
534 static void
gtk_css_node_real_node_added(GtkCssNode * parent,GtkCssNode * node,GtkCssNode * new_previous)535 gtk_css_node_real_node_added (GtkCssNode *parent,
536                               GtkCssNode *node,
537                               GtkCssNode *new_previous)
538 {
539   if (new_previous)
540     {
541       node->previous_sibling = new_previous;
542       node->next_sibling = new_previous->next_sibling;
543       new_previous->next_sibling = node;
544     }
545   else
546     {
547       node->next_sibling = parent->first_child;
548       parent->first_child = node;
549     }
550 
551   if (node->next_sibling)
552     node->next_sibling->previous_sibling = node;
553   else
554     parent->last_child = node;
555 
556   node->parent = parent;
557 }
558 
559 static void
gtk_css_node_real_style_changed(GtkCssNode * cssnode,GtkCssStyleChange * change)560 gtk_css_node_real_style_changed (GtkCssNode        *cssnode,
561                                  GtkCssStyleChange *change)
562 {
563   g_set_object (&cssnode->style, gtk_css_style_change_get_new_style (change));
564 }
565 
566 static void
gtk_css_node_class_init(GtkCssNodeClass * klass)567 gtk_css_node_class_init (GtkCssNodeClass *klass)
568 {
569   GObjectClass *object_class = G_OBJECT_CLASS (klass);
570 
571   object_class->get_property = gtk_css_node_get_property;
572   object_class->set_property = gtk_css_node_set_property;
573   object_class->dispose = gtk_css_node_dispose;
574   object_class->finalize = gtk_css_node_finalize;
575 
576   klass->update_style = gtk_css_node_real_update_style;
577   klass->invalidate = gtk_css_node_real_invalidate;
578   klass->validate = gtk_css_node_real_validate;
579   klass->queue_validate = gtk_css_node_real_queue_validate;
580   klass->dequeue_validate = gtk_css_node_real_dequeue_validate;
581   klass->init_matcher = gtk_css_node_real_init_matcher;
582   klass->create_widget_path = gtk_css_node_real_create_widget_path;
583   klass->get_widget_path = gtk_css_node_real_get_widget_path;
584   klass->get_style_provider = gtk_css_node_real_get_style_provider;
585   klass->get_frame_clock = gtk_css_node_real_get_frame_clock;
586 
587   klass->node_added = gtk_css_node_real_node_added;
588   klass->node_removed = gtk_css_node_real_node_removed;
589   klass->style_changed = gtk_css_node_real_style_changed;
590 
591   cssnode_signals[NODE_ADDED] =
592     g_signal_new (I_("node-added"),
593 		  G_TYPE_FROM_CLASS (object_class),
594 		  G_SIGNAL_RUN_LAST,
595                   G_STRUCT_OFFSET (GtkCssNodeClass, node_added),
596 		  NULL, NULL,
597 		  _gtk_marshal_VOID__OBJECT_OBJECT,
598 		  G_TYPE_NONE, 2,
599 		  GTK_TYPE_CSS_NODE, GTK_TYPE_CSS_NODE);
600   g_signal_set_va_marshaller (cssnode_signals[NODE_ADDED],
601                               G_TYPE_FROM_CLASS (klass),
602                               _gtk_marshal_VOID__OBJECT_OBJECTv);
603 
604   cssnode_signals[NODE_REMOVED] =
605     g_signal_new (I_("node-removed"),
606 		  G_TYPE_FROM_CLASS (object_class),
607 		  G_SIGNAL_RUN_LAST,
608                   G_STRUCT_OFFSET (GtkCssNodeClass, node_removed),
609 		  NULL, NULL,
610 		  _gtk_marshal_VOID__OBJECT_OBJECT,
611 		  G_TYPE_NONE, 2,
612 		  GTK_TYPE_CSS_NODE, GTK_TYPE_CSS_NODE);
613   g_signal_set_va_marshaller (cssnode_signals[NODE_REMOVED],
614                               G_TYPE_FROM_CLASS (klass),
615                               _gtk_marshal_VOID__OBJECT_OBJECTv);
616 
617   cssnode_signals[STYLE_CHANGED] =
618     g_signal_new (I_("style-changed"),
619 		  G_TYPE_FROM_CLASS (object_class),
620 		  G_SIGNAL_RUN_LAST,
621                   G_STRUCT_OFFSET (GtkCssNodeClass, style_changed),
622 		  NULL, NULL,
623 		  NULL,
624 		  G_TYPE_NONE, 1,
625 		  G_TYPE_POINTER);
626 
627   cssnode_properties[PROP_CLASSES] =
628     g_param_spec_boxed ("classes", P_("Style Classes"), P_("List of classes"),
629                          G_TYPE_STRV,
630                          G_PARAM_READWRITE
631                          | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
632   cssnode_properties[PROP_ID] =
633     g_param_spec_string ("id", P_("ID"), P_("Unique ID"),
634                          NULL,
635                          G_PARAM_READWRITE
636                          | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
637   cssnode_properties[PROP_NAME] =
638     g_param_spec_string ("name", P_("Name"), "Name identifying the type of node",
639                          NULL,
640                          G_PARAM_READWRITE
641                          | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
642   cssnode_properties[PROP_STATE] =
643     g_param_spec_flags ("state", P_("State"), P_("State flags"),
644                         GTK_TYPE_STATE_FLAGS,
645                         0,
646                         G_PARAM_READWRITE
647                         | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
648   cssnode_properties[PROP_VISIBLE] =
649     g_param_spec_boolean ("visible", P_("Visible"), P_("If other nodes can see this node"),
650                           TRUE,
651                           G_PARAM_READWRITE
652                           | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
653   cssnode_properties[PROP_WIDGET_TYPE] =
654     g_param_spec_gtype ("widget-type", P_("Widget type"), P_("GType of the widget"),
655                         G_TYPE_NONE,
656                         G_PARAM_READWRITE
657                         | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
658 
659   g_object_class_install_properties (object_class, NUM_PROPERTIES, cssnode_properties);
660 }
661 
662 static void
gtk_css_node_init(GtkCssNode * cssnode)663 gtk_css_node_init (GtkCssNode *cssnode)
664 {
665   cssnode->decl = gtk_css_node_declaration_new ();
666 
667   cssnode->style = g_object_ref (gtk_css_static_style_get_default ());
668 
669   cssnode->visible = TRUE;
670 }
671 
672 /**
673  * gtk_css_node_new:
674  *
675  * Creates a new CSS node.
676  *
677  * Returns: (transfer full): the new CSS node
678  */
679 GtkCssNode *
gtk_css_node_new(void)680 gtk_css_node_new (void)
681 {
682   return g_object_new (GTK_TYPE_CSS_NODE, NULL);
683 }
684 
685 static GdkFrameClock *
gtk_css_node_get_frame_clock_or_null(GtkCssNode * cssnode)686 gtk_css_node_get_frame_clock_or_null (GtkCssNode *cssnode)
687 {
688   while (cssnode->parent)
689     cssnode = cssnode->parent;
690 
691   return GTK_CSS_NODE_GET_CLASS (cssnode)->get_frame_clock (cssnode);
692 }
693 
694 static gint64
gtk_css_node_get_timestamp(GtkCssNode * cssnode)695 gtk_css_node_get_timestamp (GtkCssNode *cssnode)
696 {
697   GdkFrameClock *frameclock;
698 
699   frameclock = gtk_css_node_get_frame_clock_or_null (cssnode);
700   if (frameclock == NULL)
701     return 0;
702 
703   return gdk_frame_clock_get_frame_time (frameclock);
704 }
705 
706 static void
gtk_css_node_parent_was_unset(GtkCssNode * node)707 gtk_css_node_parent_was_unset (GtkCssNode *node)
708 {
709   if (node->visible && node->invalid)
710     GTK_CSS_NODE_GET_CLASS (node)->queue_validate (node);
711 }
712 
713 static void
gtk_css_node_parent_will_be_set(GtkCssNode * node)714 gtk_css_node_parent_will_be_set (GtkCssNode *node)
715 {
716   if (node->visible && node->invalid)
717     GTK_CSS_NODE_GET_CLASS (node)->dequeue_validate (node);
718 }
719 
720 static void
gtk_css_node_invalidate_style(GtkCssNode * cssnode)721 gtk_css_node_invalidate_style (GtkCssNode *cssnode)
722 {
723   if (cssnode->style_is_invalid)
724     return;
725 
726   cssnode->style_is_invalid = TRUE;
727   gtk_css_node_set_invalid (cssnode, TRUE);
728 
729   if (cssnode->first_child)
730     gtk_css_node_invalidate_style (cssnode->first_child);
731 
732   if (cssnode->next_sibling)
733     gtk_css_node_invalidate_style (cssnode->next_sibling);
734 }
735 
736 static void
gtk_css_node_reposition(GtkCssNode * node,GtkCssNode * new_parent,GtkCssNode * previous)737 gtk_css_node_reposition (GtkCssNode *node,
738                          GtkCssNode *new_parent,
739                          GtkCssNode *previous)
740 {
741   GtkCssNode *old_parent;
742 
743   g_assert (! (new_parent == NULL && previous != NULL));
744 
745   old_parent = node->parent;
746   /* Take a reference here so the whole function has a reference */
747   g_object_ref (node);
748 
749   if (node->visible)
750     {
751       if (node->next_sibling)
752         gtk_css_node_invalidate (node->next_sibling,
753                                  GTK_CSS_CHANGE_ANY_SIBLING
754                                  | GTK_CSS_CHANGE_NTH_CHILD
755                                  | (node->previous_sibling ? 0 : GTK_CSS_CHANGE_FIRST_CHILD));
756       else if (node->previous_sibling)
757         gtk_css_node_invalidate (node->previous_sibling, GTK_CSS_CHANGE_LAST_CHILD);
758     }
759 
760   if (old_parent != NULL)
761     {
762       g_signal_emit (old_parent, cssnode_signals[NODE_REMOVED], 0, node, node->previous_sibling);
763       if (old_parent->first_child && node->visible)
764         gtk_css_node_invalidate (old_parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
765     }
766 
767   if (old_parent != new_parent)
768     {
769       if (old_parent == NULL)
770         {
771           gtk_css_node_parent_will_be_set (node);
772         }
773       else
774         {
775           g_object_unref (node);
776         }
777 
778       if (gtk_css_node_get_style_provider_or_null (node) == NULL)
779         gtk_css_node_invalidate_style_provider (node);
780       gtk_css_node_invalidate (node, GTK_CSS_CHANGE_TIMESTAMP | GTK_CSS_CHANGE_ANIMATIONS);
781 
782       if (new_parent)
783         {
784           g_object_ref (node);
785 
786           if (node->pending_changes)
787             new_parent->needs_propagation = TRUE;
788           if (node->invalid && node->visible)
789             gtk_css_node_set_invalid (new_parent, TRUE);
790         }
791       else
792         {
793           gtk_css_node_parent_was_unset (node);
794         }
795     }
796 
797   if (new_parent)
798     {
799       g_signal_emit (new_parent, cssnode_signals[NODE_ADDED], 0, node, previous);
800       if (node->visible)
801         gtk_css_node_invalidate (new_parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
802     }
803 
804   if (node->visible)
805     {
806       if (node->next_sibling)
807         {
808           if (node->previous_sibling == NULL)
809             gtk_css_node_invalidate (node->next_sibling, GTK_CSS_CHANGE_FIRST_CHILD);
810           else
811             gtk_css_node_invalidate_style (node->next_sibling);
812         }
813       else if (node->previous_sibling)
814         {
815           gtk_css_node_invalidate (node->previous_sibling, GTK_CSS_CHANGE_LAST_CHILD);
816         }
817     }
818   else
819     {
820       if (node->next_sibling)
821         gtk_css_node_invalidate_style (node->next_sibling);
822     }
823 
824   gtk_css_node_invalidate (node, GTK_CSS_CHANGE_ANY_PARENT
825                                  | GTK_CSS_CHANGE_ANY_SIBLING
826                                  | GTK_CSS_CHANGE_NTH_CHILD
827                                  | (node->previous_sibling ? 0 : GTK_CSS_CHANGE_FIRST_CHILD)
828                                  | (node->next_sibling ? 0 : GTK_CSS_CHANGE_LAST_CHILD));
829 
830   g_object_unref (node);
831 }
832 
833 void
gtk_css_node_set_parent(GtkCssNode * node,GtkCssNode * parent)834 gtk_css_node_set_parent (GtkCssNode *node,
835                          GtkCssNode *parent)
836 {
837   if (node->parent == parent)
838     return;
839 
840   gtk_css_node_reposition (node, parent, parent ? parent->last_child : NULL);
841 }
842 
843 /* If previous_sibling is NULL, insert at the beginning */
844 void
gtk_css_node_insert_after(GtkCssNode * parent,GtkCssNode * cssnode,GtkCssNode * previous_sibling)845 gtk_css_node_insert_after (GtkCssNode *parent,
846                            GtkCssNode *cssnode,
847                            GtkCssNode *previous_sibling)
848 {
849   g_return_if_fail (previous_sibling == NULL || previous_sibling->parent == parent);
850   g_return_if_fail (cssnode != previous_sibling);
851 
852   if (cssnode->previous_sibling == previous_sibling &&
853       cssnode->parent == parent)
854     return;
855 
856   gtk_css_node_reposition (cssnode,
857                            parent,
858                            previous_sibling);
859 }
860 
861 /* If next_sibling is NULL, insert at the end */
862 void
gtk_css_node_insert_before(GtkCssNode * parent,GtkCssNode * cssnode,GtkCssNode * next_sibling)863 gtk_css_node_insert_before (GtkCssNode *parent,
864                             GtkCssNode *cssnode,
865                             GtkCssNode *next_sibling)
866 {
867   g_return_if_fail (next_sibling == NULL || next_sibling->parent == parent);
868   g_return_if_fail (cssnode != next_sibling);
869 
870   if (cssnode->next_sibling == next_sibling &&
871       cssnode->parent == parent)
872     return;
873 
874   gtk_css_node_reposition (cssnode,
875                            parent,
876                            next_sibling ? next_sibling->previous_sibling : parent->last_child);
877 }
878 
879 void
gtk_css_node_reverse_children(GtkCssNode * cssnode)880 gtk_css_node_reverse_children (GtkCssNode *cssnode)
881 {
882   GtkCssNode *end;
883 
884   end = cssnode->last_child;
885   while (cssnode->first_child != end)
886     {
887       gtk_css_node_reposition (cssnode->first_child,
888                                cssnode,
889                                end);
890     }
891 
892 }
893 
894 GtkCssNode *
gtk_css_node_get_parent(GtkCssNode * cssnode)895 gtk_css_node_get_parent (GtkCssNode *cssnode)
896 {
897   return cssnode->parent;
898 }
899 
900 GtkCssNode *
gtk_css_node_get_first_child(GtkCssNode * cssnode)901 gtk_css_node_get_first_child (GtkCssNode *cssnode)
902 {
903   return cssnode->first_child;
904 }
905 
906 GtkCssNode *
gtk_css_node_get_last_child(GtkCssNode * cssnode)907 gtk_css_node_get_last_child (GtkCssNode *cssnode)
908 {
909   return cssnode->last_child;
910 }
911 
912 GtkCssNode *
gtk_css_node_get_previous_sibling(GtkCssNode * cssnode)913 gtk_css_node_get_previous_sibling (GtkCssNode *cssnode)
914 {
915   return cssnode->previous_sibling;
916 }
917 
918 GtkCssNode *
gtk_css_node_get_next_sibling(GtkCssNode * cssnode)919 gtk_css_node_get_next_sibling (GtkCssNode *cssnode)
920 {
921   return cssnode->next_sibling;
922 }
923 
924 static gboolean
gtk_css_node_set_style(GtkCssNode * cssnode,GtkCssStyle * style)925 gtk_css_node_set_style (GtkCssNode  *cssnode,
926                         GtkCssStyle *style)
927 {
928   GtkCssStyleChange change;
929   gboolean style_changed;
930 
931   if (cssnode->style == style)
932     return FALSE;
933 
934   gtk_css_style_change_init (&change, cssnode->style, style);
935 
936   style_changed = gtk_css_style_change_has_change (&change);
937   if (style_changed)
938     {
939       g_signal_emit (cssnode, cssnode_signals[STYLE_CHANGED], 0, &change);
940     }
941   else if (cssnode->style != style &&
942            (GTK_IS_CSS_ANIMATED_STYLE (cssnode->style) || GTK_IS_CSS_ANIMATED_STYLE (style)))
943     {
944       /* This is when animations are starting/stopping but they didn't change any CSS this frame */
945       g_set_object (&cssnode->style, style);
946     }
947 
948   gtk_css_style_change_finish (&change);
949 
950   return style_changed;
951 }
952 
953 static void
gtk_css_node_propagate_pending_changes(GtkCssNode * cssnode,gboolean style_changed)954 gtk_css_node_propagate_pending_changes (GtkCssNode *cssnode,
955                                         gboolean    style_changed)
956 {
957   GtkCssChange change, child_change;
958   GtkCssNode *child;
959 
960   change = _gtk_css_change_for_child (cssnode->pending_changes);
961   if (style_changed)
962     change |= GTK_CSS_CHANGE_PARENT_STYLE;
963 
964   if (!cssnode->needs_propagation && change == 0)
965     return;
966 
967   for (child = gtk_css_node_get_first_child (cssnode);
968        child;
969        child = gtk_css_node_get_next_sibling (child))
970     {
971       child_change = child->pending_changes;
972       gtk_css_node_invalidate (child, change);
973       if (child->visible)
974         change |= _gtk_css_change_for_sibling (child_change);
975     }
976 
977   cssnode->needs_propagation = FALSE;
978 }
979 
980 static gboolean
gtk_css_node_needs_new_style(GtkCssNode * cssnode)981 gtk_css_node_needs_new_style (GtkCssNode *cssnode)
982 {
983   return cssnode->style_is_invalid || cssnode->needs_propagation;
984 }
985 
986 static void
gtk_css_node_ensure_style(GtkCssNode * cssnode,gint64 current_time)987 gtk_css_node_ensure_style (GtkCssNode *cssnode,
988                            gint64      current_time)
989 {
990   gboolean style_changed;
991 
992   if (!gtk_css_node_needs_new_style (cssnode))
993     return;
994 
995   if (cssnode->parent)
996     gtk_css_node_ensure_style (cssnode->parent, current_time);
997 
998   if (cssnode->style_is_invalid)
999     {
1000       GtkCssStyle *new_style;
1001 
1002       if (cssnode->previous_sibling)
1003         gtk_css_node_ensure_style (cssnode->previous_sibling, current_time);
1004 
1005       g_clear_pointer (&cssnode->cache, gtk_css_node_style_cache_unref);
1006 
1007       new_style = GTK_CSS_NODE_GET_CLASS (cssnode)->update_style (cssnode,
1008                                                                   cssnode->pending_changes,
1009                                                                   current_time,
1010                                                                   cssnode->style);
1011 
1012       style_changed = gtk_css_node_set_style (cssnode, new_style);
1013       g_object_unref (new_style);
1014     }
1015   else
1016     {
1017       style_changed = FALSE;
1018     }
1019 
1020   gtk_css_node_propagate_pending_changes (cssnode, style_changed);
1021 
1022   cssnode->pending_changes = 0;
1023   cssnode->style_is_invalid = FALSE;
1024 }
1025 
1026 GtkCssStyle *
gtk_css_node_get_style(GtkCssNode * cssnode)1027 gtk_css_node_get_style (GtkCssNode *cssnode)
1028 {
1029   if (gtk_css_node_needs_new_style (cssnode))
1030     {
1031       gint64 timestamp = gtk_css_node_get_timestamp (cssnode);
1032 
1033       gtk_css_node_ensure_style (cssnode, timestamp);
1034     }
1035 
1036   return cssnode->style;
1037 }
1038 
1039 void
gtk_css_node_set_visible(GtkCssNode * cssnode,gboolean visible)1040 gtk_css_node_set_visible (GtkCssNode *cssnode,
1041                           gboolean    visible)
1042 {
1043   GtkCssNode *iter;
1044 
1045   if (cssnode->visible == visible)
1046     return;
1047 
1048   cssnode->visible = visible;
1049   g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_VISIBLE]);
1050 
1051   if (cssnode->invalid)
1052     {
1053       if (cssnode->visible)
1054         {
1055           if (cssnode->parent)
1056             gtk_css_node_set_invalid (cssnode->parent, TRUE);
1057           else
1058             GTK_CSS_NODE_GET_CLASS (cssnode)->queue_validate (cssnode);
1059         }
1060       else
1061         {
1062           if (cssnode->parent == NULL)
1063             GTK_CSS_NODE_GET_CLASS (cssnode)->dequeue_validate (cssnode);
1064         }
1065     }
1066 
1067   if (cssnode->next_sibling)
1068     {
1069       gtk_css_node_invalidate (cssnode->next_sibling, GTK_CSS_CHANGE_ANY_SIBLING | GTK_CSS_CHANGE_NTH_CHILD);
1070       if (gtk_css_node_is_first_child (cssnode))
1071         {
1072           for (iter = cssnode->next_sibling;
1073                iter != NULL;
1074                iter = iter->next_sibling)
1075             {
1076               gtk_css_node_invalidate (iter, GTK_CSS_CHANGE_FIRST_CHILD);
1077               if (iter->visible)
1078                 break;
1079             }
1080         }
1081     }
1082 
1083   if (cssnode->previous_sibling)
1084     {
1085       if (gtk_css_node_is_last_child (cssnode))
1086         {
1087           for (iter = cssnode->previous_sibling;
1088                iter != NULL;
1089                iter = iter->previous_sibling)
1090             {
1091               gtk_css_node_invalidate (iter, GTK_CSS_CHANGE_LAST_CHILD);
1092               if (iter->visible)
1093                 break;
1094             }
1095         }
1096       gtk_css_node_invalidate (cssnode->parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
1097     }
1098 }
1099 
1100 gboolean
gtk_css_node_get_visible(GtkCssNode * cssnode)1101 gtk_css_node_get_visible (GtkCssNode *cssnode)
1102 {
1103   return cssnode->visible;
1104 }
1105 
1106 void
gtk_css_node_set_name(GtkCssNode * cssnode,const char * name)1107 gtk_css_node_set_name (GtkCssNode              *cssnode,
1108                        /*interned*/ const char *name)
1109 {
1110   if (gtk_css_node_declaration_set_name (&cssnode->decl, name))
1111     {
1112       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_NAME);
1113       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_NAME]);
1114     }
1115 }
1116 
1117 /* interned */ const char *
gtk_css_node_get_name(GtkCssNode * cssnode)1118 gtk_css_node_get_name (GtkCssNode *cssnode)
1119 {
1120   return gtk_css_node_declaration_get_name (cssnode->decl);
1121 }
1122 
1123 void
gtk_css_node_set_widget_type(GtkCssNode * cssnode,GType widget_type)1124 gtk_css_node_set_widget_type (GtkCssNode *cssnode,
1125                               GType       widget_type)
1126 {
1127   if (gtk_css_node_declaration_set_type (&cssnode->decl, widget_type))
1128     {
1129       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_NAME);
1130       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_WIDGET_TYPE]);
1131     }
1132 }
1133 
1134 GType
gtk_css_node_get_widget_type(GtkCssNode * cssnode)1135 gtk_css_node_get_widget_type (GtkCssNode *cssnode)
1136 {
1137   return gtk_css_node_declaration_get_type (cssnode->decl);
1138 }
1139 
1140 void
gtk_css_node_set_id(GtkCssNode * cssnode,const char * id)1141 gtk_css_node_set_id (GtkCssNode                *cssnode,
1142                      /* interned */ const char *id)
1143 {
1144   if (gtk_css_node_declaration_set_id (&cssnode->decl, id))
1145     {
1146       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_ID);
1147       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_ID]);
1148     }
1149 }
1150 
1151 /* interned */ const char *
gtk_css_node_get_id(GtkCssNode * cssnode)1152 gtk_css_node_get_id (GtkCssNode *cssnode)
1153 {
1154   return gtk_css_node_declaration_get_id (cssnode->decl);
1155 }
1156 
1157 void
gtk_css_node_set_state(GtkCssNode * cssnode,GtkStateFlags state_flags)1158 gtk_css_node_set_state (GtkCssNode    *cssnode,
1159                         GtkStateFlags  state_flags)
1160 {
1161   if (gtk_css_node_declaration_set_state (&cssnode->decl, state_flags))
1162     {
1163       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_STATE);
1164       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_STATE]);
1165     }
1166 }
1167 
1168 GtkStateFlags
gtk_css_node_get_state(GtkCssNode * cssnode)1169 gtk_css_node_get_state (GtkCssNode *cssnode)
1170 {
1171   return gtk_css_node_declaration_get_state (cssnode->decl);
1172 }
1173 
1174 void
gtk_css_node_set_junction_sides(GtkCssNode * cssnode,GtkJunctionSides junction_sides)1175 gtk_css_node_set_junction_sides (GtkCssNode       *cssnode,
1176                                  GtkJunctionSides  junction_sides)
1177 {
1178   gtk_css_node_declaration_set_junction_sides (&cssnode->decl, junction_sides);
1179 }
1180 
1181 GtkJunctionSides
gtk_css_node_get_junction_sides(GtkCssNode * cssnode)1182 gtk_css_node_get_junction_sides (GtkCssNode *cssnode)
1183 {
1184   return gtk_css_node_declaration_get_junction_sides (cssnode->decl);
1185 }
1186 
1187 static void
gtk_css_node_clear_classes(GtkCssNode * cssnode)1188 gtk_css_node_clear_classes (GtkCssNode *cssnode)
1189 {
1190   if (gtk_css_node_declaration_clear_classes (&cssnode->decl))
1191     {
1192       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_CLASS);
1193       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_CLASSES]);
1194     }
1195 }
1196 
1197 void
gtk_css_node_set_classes(GtkCssNode * cssnode,const char ** classes)1198 gtk_css_node_set_classes (GtkCssNode  *cssnode,
1199                           const char **classes)
1200 {
1201   guint i;
1202 
1203   g_object_freeze_notify (G_OBJECT (cssnode));
1204 
1205   gtk_css_node_clear_classes (cssnode);
1206 
1207   if (classes)
1208     {
1209       for (i = 0; classes[i] != NULL; i++)
1210         {
1211           gtk_css_node_add_class (cssnode, g_quark_from_string (classes[i]));
1212         }
1213     }
1214 
1215   g_object_thaw_notify (G_OBJECT (cssnode));
1216 }
1217 
1218 char **
gtk_css_node_get_classes(GtkCssNode * cssnode)1219 gtk_css_node_get_classes (GtkCssNode *cssnode)
1220 {
1221   const GQuark *classes;
1222   char **result;
1223   guint n_classes, i, j;
1224 
1225   classes = gtk_css_node_declaration_get_classes (cssnode->decl, &n_classes);
1226   result = g_new (char *, n_classes + 1);
1227 
1228   for (i = n_classes, j = 0; i-- > 0; ++j)
1229     {
1230       result[j] = g_strdup (g_quark_to_string (classes[i]));
1231     }
1232 
1233   result[n_classes] = NULL;
1234   return result;
1235 }
1236 
1237 void
gtk_css_node_add_class(GtkCssNode * cssnode,GQuark style_class)1238 gtk_css_node_add_class (GtkCssNode *cssnode,
1239                         GQuark      style_class)
1240 {
1241   if (gtk_css_node_declaration_add_class (&cssnode->decl, style_class))
1242     {
1243       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_CLASS);
1244       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_CLASSES]);
1245     }
1246 }
1247 
1248 void
gtk_css_node_remove_class(GtkCssNode * cssnode,GQuark style_class)1249 gtk_css_node_remove_class (GtkCssNode *cssnode,
1250                            GQuark      style_class)
1251 {
1252   if (gtk_css_node_declaration_remove_class (&cssnode->decl, style_class))
1253     {
1254       gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_CLASS);
1255       g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_CLASSES]);
1256     }
1257 }
1258 
1259 gboolean
gtk_css_node_has_class(GtkCssNode * cssnode,GQuark style_class)1260 gtk_css_node_has_class (GtkCssNode *cssnode,
1261                         GQuark      style_class)
1262 {
1263   return gtk_css_node_declaration_has_class (cssnode->decl, style_class);
1264 }
1265 
1266 const GQuark *
gtk_css_node_list_classes(GtkCssNode * cssnode,guint * n_classes)1267 gtk_css_node_list_classes (GtkCssNode *cssnode,
1268                            guint      *n_classes)
1269 {
1270   return gtk_css_node_declaration_get_classes (cssnode->decl, n_classes);
1271 }
1272 
1273 void
gtk_css_node_add_region(GtkCssNode * cssnode,GQuark region,GtkRegionFlags flags)1274 gtk_css_node_add_region (GtkCssNode     *cssnode,
1275                          GQuark          region,
1276                          GtkRegionFlags  flags)
1277 {
1278   gtk_css_node_declaration_add_region (&cssnode->decl, region, flags);
1279 }
1280 
1281 void
gtk_css_node_remove_region(GtkCssNode * cssnode,GQuark region)1282 gtk_css_node_remove_region (GtkCssNode *cssnode,
1283                             GQuark      region)
1284 {
1285   gtk_css_node_declaration_remove_region (&cssnode->decl, region);
1286 }
1287 
1288 gboolean
gtk_css_node_has_region(GtkCssNode * cssnode,GQuark region,GtkRegionFlags * out_flags)1289 gtk_css_node_has_region (GtkCssNode     *cssnode,
1290                          GQuark          region,
1291                          GtkRegionFlags *out_flags)
1292 {
1293   return gtk_css_node_declaration_has_region (cssnode->decl, region, out_flags);
1294 }
1295 
1296 GList *
gtk_css_node_list_regions(GtkCssNode * cssnode)1297 gtk_css_node_list_regions (GtkCssNode *cssnode)
1298 {
1299   return gtk_css_node_declaration_list_regions (cssnode->decl);
1300 }
1301 
1302 
1303 const GtkCssNodeDeclaration *
gtk_css_node_get_declaration(GtkCssNode * cssnode)1304 gtk_css_node_get_declaration (GtkCssNode *cssnode)
1305 {
1306   return cssnode->decl;
1307 }
1308 
1309 void
gtk_css_node_invalidate_style_provider(GtkCssNode * cssnode)1310 gtk_css_node_invalidate_style_provider (GtkCssNode *cssnode)
1311 {
1312   GtkCssNode *child;
1313 
1314   gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_SOURCE);
1315 
1316   for (child = cssnode->first_child;
1317        child;
1318        child = child->next_sibling)
1319     {
1320       if (gtk_css_node_get_style_provider_or_null (child) == NULL)
1321         gtk_css_node_invalidate_style_provider (child);
1322     }
1323 }
1324 
1325 static void
gtk_css_node_invalidate_timestamp(GtkCssNode * cssnode)1326 gtk_css_node_invalidate_timestamp (GtkCssNode *cssnode)
1327 {
1328   GtkCssNode *child;
1329 
1330   if (!cssnode->invalid)
1331     return;
1332 
1333   if (!gtk_css_style_is_static (cssnode->style))
1334     gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_TIMESTAMP);
1335 
1336   for (child = cssnode->first_child; child; child = child->next_sibling)
1337     {
1338       gtk_css_node_invalidate_timestamp (child);
1339     }
1340 }
1341 
1342 void
gtk_css_node_invalidate_frame_clock(GtkCssNode * cssnode,gboolean just_timestamp)1343 gtk_css_node_invalidate_frame_clock (GtkCssNode *cssnode,
1344                                      gboolean    just_timestamp)
1345 {
1346   /* frame clock is handled by the top level */
1347   if (cssnode->parent)
1348     return;
1349 
1350   gtk_css_node_invalidate_timestamp (cssnode);
1351 
1352   if (!just_timestamp)
1353     gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_ANIMATIONS);
1354 }
1355 
1356 void
gtk_css_node_invalidate(GtkCssNode * cssnode,GtkCssChange change)1357 gtk_css_node_invalidate (GtkCssNode   *cssnode,
1358                          GtkCssChange  change)
1359 {
1360   if (!cssnode->invalid)
1361     change &= ~GTK_CSS_CHANGE_TIMESTAMP;
1362 
1363   if (change == 0)
1364     return;
1365 
1366   cssnode->pending_changes |= change;
1367 
1368   GTK_CSS_NODE_GET_CLASS (cssnode)->invalidate (cssnode);
1369 
1370   if (cssnode->parent)
1371     cssnode->parent->needs_propagation = TRUE;
1372   gtk_css_node_invalidate_style (cssnode);
1373 }
1374 
1375 void
gtk_css_node_validate_internal(GtkCssNode * cssnode,gint64 timestamp)1376 gtk_css_node_validate_internal (GtkCssNode *cssnode,
1377                                 gint64      timestamp)
1378 {
1379   GtkCssNode *child;
1380 
1381   if (!cssnode->invalid)
1382     return;
1383 
1384   gtk_css_node_ensure_style (cssnode, timestamp);
1385 
1386   /* need to set to FALSE then to TRUE here to make it chain up */
1387   gtk_css_node_set_invalid (cssnode, FALSE);
1388   if (!gtk_css_style_is_static (cssnode->style))
1389     gtk_css_node_set_invalid (cssnode, TRUE);
1390 
1391   GTK_CSS_NODE_GET_CLASS (cssnode)->validate (cssnode);
1392 
1393   for (child = gtk_css_node_get_first_child (cssnode);
1394        child;
1395        child = gtk_css_node_get_next_sibling (child))
1396     {
1397       if (child->visible)
1398         gtk_css_node_validate_internal (child, timestamp);
1399     }
1400 }
1401 
1402 void
gtk_css_node_validate(GtkCssNode * cssnode)1403 gtk_css_node_validate (GtkCssNode *cssnode)
1404 {
1405   gint64 timestamp;
1406 
1407   timestamp = gtk_css_node_get_timestamp (cssnode);
1408 
1409   gtk_css_node_validate_internal (cssnode, timestamp);
1410 }
1411 
1412 gboolean
gtk_css_node_init_matcher(GtkCssNode * cssnode,GtkCssMatcher * matcher)1413 gtk_css_node_init_matcher (GtkCssNode     *cssnode,
1414                            GtkCssMatcher  *matcher)
1415 {
1416   return GTK_CSS_NODE_GET_CLASS (cssnode)->init_matcher (cssnode, matcher);
1417 }
1418 
1419 GtkWidgetPath *
gtk_css_node_create_widget_path(GtkCssNode * cssnode)1420 gtk_css_node_create_widget_path (GtkCssNode *cssnode)
1421 {
1422   return GTK_CSS_NODE_GET_CLASS (cssnode)->create_widget_path (cssnode);
1423 }
1424 
1425 const GtkWidgetPath *
gtk_css_node_get_widget_path(GtkCssNode * cssnode)1426 gtk_css_node_get_widget_path (GtkCssNode *cssnode)
1427 {
1428   return GTK_CSS_NODE_GET_CLASS (cssnode)->get_widget_path (cssnode);
1429 }
1430 
1431 GtkStyleProviderPrivate *
gtk_css_node_get_style_provider(GtkCssNode * cssnode)1432 gtk_css_node_get_style_provider (GtkCssNode *cssnode)
1433 {
1434   GtkStyleProviderPrivate *result;
1435   GtkSettings *settings;
1436 
1437   result = gtk_css_node_get_style_provider_or_null (cssnode);
1438   if (result)
1439     return result;
1440 
1441   if (cssnode->parent)
1442     return gtk_css_node_get_style_provider (cssnode->parent);
1443 
1444   settings = gtk_settings_get_default ();
1445   if (!settings)
1446     return NULL;
1447 
1448   return GTK_STYLE_PROVIDER_PRIVATE (_gtk_settings_get_style_cascade (settings, 1));
1449 }
1450 
1451 void
gtk_css_node_print(GtkCssNode * cssnode,GtkStyleContextPrintFlags flags,GString * string,guint indent)1452 gtk_css_node_print (GtkCssNode                *cssnode,
1453                     GtkStyleContextPrintFlags  flags,
1454                     GString                   *string,
1455                     guint                      indent)
1456 {
1457   gboolean need_newline = FALSE;
1458 
1459   g_string_append_printf (string, "%*s", indent, "");
1460 
1461   if (!cssnode->visible)
1462     g_string_append_c (string, '[');
1463 
1464   gtk_css_node_declaration_print (cssnode->decl, string);
1465 
1466   if (!cssnode->visible)
1467     g_string_append_c (string, ']');
1468 
1469   g_string_append_c (string, '\n');
1470 
1471   if (flags & GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE)
1472     need_newline = gtk_css_style_print (gtk_css_node_get_style (cssnode), string, indent + 2, TRUE);
1473 
1474   if (flags & GTK_STYLE_CONTEXT_PRINT_RECURSE)
1475     {
1476       GtkCssNode *node;
1477 
1478       if (need_newline && gtk_css_node_get_first_child (cssnode))
1479         g_string_append_c (string, '\n');
1480 
1481       for (node = gtk_css_node_get_first_child (cssnode); node; node = gtk_css_node_get_next_sibling (node))
1482         gtk_css_node_print (node, flags, string, indent + 2);
1483     }
1484 }
1485