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

source code of gtk/gtk/gtkcssnode.c