]> Pileus Git - ~andy/gtk/blob - gtk/gtkstylecontext.c
stylecontext: Keep the main style data around until revalidation
[~andy/gtk] / gtk / gtkstylecontext.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@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 <gdk/gdk.h>
21 #include <math.h>
22 #include <stdlib.h>
23 #include <gobject/gvaluecollector.h>
24
25 #include "gtkstylecontextprivate.h"
26 #include "gtkcssrgbavalueprivate.h"
27 #include "gtkstylepropertiesprivate.h"
28 #include "gtktypebuiltins.h"
29 #include "gtkthemingengineprivate.h"
30 #include "gtkintl.h"
31 #include "gtkwidget.h"
32 #include "gtkwindow.h"
33 #include "gtkprivate.h"
34 #include "gtksymboliccolorprivate.h"
35 #include "gtkanimationdescription.h"
36 #include "gtkcssnumbervalueprivate.h"
37 #include "gtktimeline.h"
38 #include "gtkiconfactory.h"
39 #include "gtkwidgetpath.h"
40 #include "gtkwidgetprivate.h"
41 #include "gtkstylecascadeprivate.h"
42 #include "gtkstyleproviderprivate.h"
43 #include "gtksettings.h"
44
45 /**
46  * SECTION:gtkstylecontext
47  * @Short_description: Rendering UI elements
48  * @Title: GtkStyleContext
49  *
50  * #GtkStyleContext is an object that stores styling information affecting
51  * a widget defined by #GtkWidgetPath.
52  *
53  * In order to construct the final style information, #GtkStyleContext
54  * queries information from all attached #GtkStyleProviders. Style providers
55  * can be either attached explicitly to the context through
56  * gtk_style_context_add_provider(), or to the screen through
57  * gtk_style_context_add_provider_for_screen(). The resulting style is a
58  * combination of all providers' information in priority order.
59  *
60  * For GTK+ widgets, any #GtkStyleContext returned by
61  * gtk_widget_get_style_context() will already have a #GtkWidgetPath, a
62  * #GdkScreen and RTL/LTR information set. The style context will be also
63  * updated automatically if any of these settings change on the widget.
64  *
65  * If you are using the theming layer standalone, you will need to set a
66  * widget path and a screen yourself to the created style context through
67  * gtk_style_context_set_path() and gtk_style_context_set_screen(), as well
68  * as updating the context yourself using gtk_style_context_invalidate()
69  * whenever any of the conditions change, such as a change in the
70  * #GtkSettings:gtk-theme-name setting or a hierarchy change in the rendered
71  * widget.
72  *
73  * <refsect2 id="gtkstylecontext-animations">
74  * <title>Transition animations</title>
75  * <para>
76  * #GtkStyleContext has built-in support for state change transitions.
77  * Note that these animations respect the #GtkSettings:gtk-enable-animations
78  * setting.
79  * </para>
80  * <para>
81  * For simple widgets where state changes affect the whole widget area,
82  * calling gtk_style_context_notify_state_change() with a %NULL region
83  * is sufficient to trigger the transition animation. And GTK+ already
84  * does that when gtk_widget_set_state() or gtk_widget_set_state_flags()
85  * are called.
86  * </para>
87  * <para>
88  * If a widget needs to declare several animatable regions (i.e. not
89  * affecting the whole widget area), its #GtkWidget::draw signal handler
90  * needs to wrap the render operations for the different regions with
91  * calls to gtk_style_context_push_animatable_region() and
92  * gtk_style_context_pop_animatable_region(). These functions take an
93  * identifier for the region which must be unique within the style context.
94  * For simple widgets with a fixed set of animatable regions, using an
95  * enumeration works well:
96  * </para>
97  * <example>
98  * <title>Using an enumeration to identify  animatable regions</title>
99  * <programlisting>
100  * enum {
101  *   REGION_ENTRY,
102  *   REGION_BUTTON_UP,
103  *   REGION_BUTTON_DOWN
104  * };
105  *
106  * ...
107  *
108  * gboolean
109  * spin_button_draw (GtkWidget *widget,
110  *                   cairo_t   *cr)
111  * {
112  *   GtkStyleContext *context;
113  *
114  *   context = gtk_widget_get_style_context (widget);
115  *
116  *   gtk_style_context_push_animatable_region (context,
117  *                                             GUINT_TO_POINTER (REGION_ENTRY));
118  *
119  *   gtk_render_background (cr, 0, 0, 100, 30);
120  *   gtk_render_frame (cr, 0, 0, 100, 30);
121  *
122  *   gtk_style_context_pop_animatable_region (context);
123  *
124  *   ...
125  * }
126  * </programlisting>
127  * </example>
128  * <para>
129  * For complex widgets with an arbitrary number of animatable regions, it
130  * is up to the implementation to come up with a way to uniquely identify
131  * each animatable region. Using pointers to internal structs is one way
132  * to achieve this:
133  * </para>
134  * <example>
135  * <title>Using struct pointers to identify animatable regions</title>
136  * <programlisting>
137  * void
138  * notebook_draw_tab (GtkWidget    *widget,
139  *                    NotebookPage *page,
140  *                    cairo_t      *cr)
141  * {
142  *   gtk_style_context_push_animatable_region (context, page);
143  *   gtk_render_extension (cr, page->x, page->y, page->width, page->height);
144  *   gtk_style_context_pop_animatable_region (context);
145  * }
146  * </programlisting>
147  * </example>
148  * <para>
149  * The widget also needs to notify the style context about a state change
150  * for a given animatable region so the animation is triggered.
151  * </para>
152  * <example>
153  * <title>Triggering a state change animation on a region</title>
154  * <programlisting>
155  * gboolean
156  * notebook_motion_notify (GtkWidget      *widget,
157  *                         GdkEventMotion *event)
158  * {
159  *   GtkStyleContext *context;
160  *   NotebookPage *page;
161  *
162  *   context = gtk_widget_get_style_context (widget);
163  *   page = find_page_under_pointer (widget, event);
164  *   gtk_style_context_notify_state_change (context,
165  *                                          gtk_widget_get_window (widget),
166  *                                          page,
167  *                                          GTK_STATE_PRELIGHT,
168  *                                          TRUE);
169  *   ...
170  * }
171  * </programlisting>
172  * </example>
173  * <para>
174  * gtk_style_context_notify_state_change() accepts %NULL region IDs as a
175  * special value, in this case, the whole widget area will be updated
176  * by the animation.
177  * </para>
178  * </refsect2>
179  * <refsect2 id="gtkstylecontext-classes">
180  * <title>Style classes and regions</title>
181  * <para>
182  * Widgets can add style classes to their context, which can be used
183  * to associate different styles by class (see <xref linkend="gtkcssprovider-selectors"/>). Theme engines can also use style classes to vary their
184  * rendering. GTK+ has a number of predefined style classes:
185  * #GTK_STYLE_CLASS_CELL,
186  * #GTK_STYLE_CLASS_ENTRY,
187  * #GTK_STYLE_CLASS_BUTTON,
188  * #GTK_STYLE_CLASS_COMBOBOX_ENTRY,
189  * #GTK_STYLE_CLASS_CALENDAR,
190  * #GTK_STYLE_CLASS_SLIDER,
191  * #GTK_STYLE_CLASS_BACKGROUND,
192  * #GTK_STYLE_CLASS_RUBBERBAND,
193  * #GTK_STYLE_CLASS_TOOLTIP,
194  * #GTK_STYLE_CLASS_MENU,
195  * #GTK_STYLE_CLASS_MENUBAR,
196  * #GTK_STYLE_CLASS_MENUITEM,
197  * #GTK_STYLE_CLASS_TOOLBAR,
198  * #GTK_STYLE_CLASS_PRIMARY_TOOLBAR,
199  * #GTK_STYLE_CLASS_INLINE_TOOLBAR,
200  * #GTK_STYLE_CLASS_RADIO,
201  * #GTK_STYLE_CLASS_CHECK,
202  * #GTK_STYLE_CLASS_TROUGH,
203  * #GTK_STYLE_CLASS_SCROLLBAR,
204  * #GTK_STYLE_CLASS_SCALE,
205  * #GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE,
206  * #GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW,
207  * #GTK_STYLE_CLASS_HEADER,
208  * #GTK_STYLE_CLASS_ACCELERATOR,
209  * #GTK_STYLE_CLASS_GRIP,
210  * #GTK_STYLE_CLASS_DOCK,
211  * #GTK_STYLE_CLASS_PROGRESSBAR,
212  * #GTK_STYLE_CLASS_SPINNER,
213  * #GTK_STYLE_CLASS_EXPANDER,
214  * #GTK_STYLE_CLASS_SPINBUTTON,
215  * #GTK_STYLE_CLASS_NOTEBOOK,
216  * #GTK_STYLE_CLASS_VIEW,
217  * #GTK_STYLE_CLASS_SIDEBAR,
218  * #GTK_STYLE_CLASS_IMAGE,
219  * #GTK_STYLE_CLASS_HIGHLIGHT,
220  * #GTK_STYLE_CLASS_FRAME,
221  * #GTK_STYLE_CLASS_DND,
222  * #GTK_STYLE_CLASS_PANE_SEPARATOR,
223  * #GTK_STYLE_CLASS_SEPARATOR,
224  * #GTK_STYLE_CLASS_INFO,
225  * #GTK_STYLE_CLASS_WARNING,
226  * #GTK_STYLE_CLASS_QUESTION,
227  * #GTK_STYLE_CLASS_ERROR,
228  * #GTK_STYLE_CLASS_HORIZONTAL,
229  * #GTK_STYLE_CLASS_VERTICAL,
230  * #GTK_STYLE_CLASS_TOP,
231  * #GTK_STYLE_CLASS_BOTTOM,
232  * #GTK_STYLE_CLASS_LEFT,
233  * #GTK_STYLE_CLASS_RIGHT,
234  * </para>
235  * <para>
236  * Widgets can also add regions with flags to their context.
237  * The regions used by GTK+ widgets are:
238  * <informaltable>
239  *   <tgroup cols="4">
240  *     <thead>
241  *       <row>
242  *         <entry>Region</entry>
243  *         <entry>Flags</entry>
244  *         <entry>Macro</entry>
245  *         <entry>Used by</entry>
246  *       </row>
247  *     </thead>
248  *     <tbody>
249  *       <row>
250  *         <entry>row</entry>
251  *         <entry>even, odd</entry>
252  *         <entry>GTK_STYLE_REGION_ROW</entry>
253  *         <entry>#GtkTreeView</entry>
254  *       </row>
255  *       <row>
256  *         <entry>column</entry>
257  *         <entry>first, last, sorted</entry>
258  *         <entry>GTK_STYLE_REGION_COLUMN</entry>
259  *         <entry>#GtkTreeView</entry>
260  *       </row>
261  *       <row>
262  *         <entry>column-header</entry>
263  *         <entry></entry>
264  *         <entry>GTK_STYLE_REGION_COLUMN_HEADER</entry>
265  *         <entry></entry>
266  *       </row>
267  *       <row>
268  *         <entry>tab</entry>
269  *         <entry>even, odd, first, last</entry>
270  *         <entry>GTK_STYLE_REGION_TAB</entry>
271  *         <entry>#GtkNotebook</entry>
272  *       </row>
273  *     </tbody>
274  *   </tgroup>
275  * </informaltable>
276  * </para>
277  * </refsect2>
278  * <refsect2 id="gtkstylecontext-custom-styling">
279  * <title>Custom styling in UI libraries and applications</title>
280  * <para>
281  * If you are developing a library with custom #GtkWidget<!-- -->s that
282  * render differently than standard components, you may need to add a
283  * #GtkStyleProvider yourself with the %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK
284  * priority, either a #GtkCssProvider or a custom object implementing the
285  * #GtkStyleProvider interface. This way theming engines may still attempt
286  * to style your UI elements in a different way if needed so.
287  * </para>
288  * <para>
289  * If you are using custom styling on an applications, you probably want then
290  * to make your style information prevail to the theme's, so you must use
291  * a #GtkStyleProvider with the %GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
292  * priority, keep in mind that the user settings in
293  * <filename><replaceable>XDG_CONFIG_HOME</replaceable>/gtk-3.0/gtk.css</filename> will
294  * still take precedence over your changes, as it uses the
295  * %GTK_STYLE_PROVIDER_PRIORITY_USER priority.
296  * </para>
297  * <para>
298  * If a custom theming engine is needed, you probably want to implement a
299  * #GtkStyleProvider yourself so it points to your #GtkThemingEngine
300  * implementation, as #GtkCssProvider uses gtk_theming_engine_load()
301  * which loads the theming engine module from the standard paths.
302  * </para>
303  * </refsect2>
304  */
305
306 /* When these change we do a full restyling. Otherwise we try to figure out
307  * if we need to change things. */
308 #define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS)
309 /* When these change we don't clear the cache. This takes more memory but makes
310  * things go faster. */
311 #define GTK_STYLE_CONTEXT_CACHED_CHANGE (GTK_CSS_CHANGE_STATE)
312
313 typedef struct GtkStyleInfo GtkStyleInfo;
314 typedef struct GtkRegion GtkRegion;
315 typedef struct PropertyValue PropertyValue;
316 typedef struct AnimationInfo AnimationInfo;
317 typedef struct StyleData StyleData;
318
319 struct GtkRegion
320 {
321   GQuark class_quark;
322   GtkRegionFlags flags;
323 };
324
325 struct PropertyValue
326 {
327   GType       widget_type;
328   GParamSpec *pspec;
329   GtkStateFlags state;
330   GValue      value;
331 };
332
333 struct GtkStyleInfo
334 {
335   GArray *style_classes;
336   GArray *regions;
337   GtkJunctionSides junction_sides;
338   GtkStateFlags state_flags;
339   StyleData *data;
340 };
341
342 struct StyleData
343 {
344   GtkCssComputedValues *store;
345   GArray *property_cache;
346 };
347
348 struct AnimationInfo
349 {
350   GtkTimeline *timeline;
351
352   gpointer region_id;
353
354   /* Region stack (until region_id) at the time of
355    * rendering, this is used for nested cancellation.
356    */
357   GSList *parent_regions;
358
359   GdkWindow *window;
360   GtkStateType state;
361   gboolean target_value;
362
363   cairo_region_t *invalidation_region;
364   GArray *rectangles;
365 };
366
367 struct _GtkStyleContextPrivate
368 {
369   GdkScreen *screen;
370
371   GtkStyleCascade *cascade;
372
373   GtkStyleContext *parent;
374   GSList *children;
375   GtkWidget *widget;            
376   GtkWidgetPath *widget_path;
377   GHashTable *style_data;
378   GSList *info_stack;
379
380   GSList *animation_regions;
381   GSList *animations;
382
383   GtkThemingEngine *theming_engine;
384
385   GtkTextDirection direction;
386
387   GtkCssChange relevant_changes;
388   GtkCssChange pending_changes;
389
390   guint animations_invalidated : 1;
391   guint invalidating_context : 1;
392   guint invalid : 1;
393 };
394
395 enum {
396   PROP_0,
397   PROP_SCREEN,
398   PROP_DIRECTION,
399   PROP_PARENT
400 };
401
402 enum {
403   CHANGED,
404   LAST_SIGNAL
405 };
406
407 static guint signals[LAST_SIGNAL] = { 0 };
408
409 static void gtk_style_context_finalize (GObject *object);
410
411 static void gtk_style_context_impl_set_property (GObject      *object,
412                                                  guint         prop_id,
413                                                  const GValue *value,
414                                                  GParamSpec   *pspec);
415 static void gtk_style_context_impl_get_property (GObject      *object,
416                                                  guint         prop_id,
417                                                  GValue       *value,
418                                                  GParamSpec   *pspec);
419 static GtkSymbolicColor *
420             gtk_style_context_color_lookup_func (gpointer      contextp,
421                                                  const char   *name);
422
423
424 G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
425
426 static void
427 gtk_style_context_real_changed (GtkStyleContext *context)
428 {
429   GtkStyleContextPrivate *priv = context->priv;
430
431   if (priv->widget)
432     _gtk_widget_style_context_invalidated (priv->widget);
433 }
434
435 static void
436 gtk_style_context_class_init (GtkStyleContextClass *klass)
437 {
438   GObjectClass *object_class = G_OBJECT_CLASS (klass);
439
440   object_class->finalize = gtk_style_context_finalize;
441   object_class->set_property = gtk_style_context_impl_set_property;
442   object_class->get_property = gtk_style_context_impl_get_property;
443
444   klass->changed = gtk_style_context_real_changed;
445
446   signals[CHANGED] =
447     g_signal_new (I_("changed"),
448                   G_TYPE_FROM_CLASS (object_class),
449                   G_SIGNAL_RUN_FIRST,
450                   G_STRUCT_OFFSET (GtkStyleContextClass, changed),
451                   NULL, NULL,
452                   g_cclosure_marshal_VOID__VOID,
453                   G_TYPE_NONE, 0);
454
455   g_object_class_install_property (object_class,
456                                    PROP_SCREEN,
457                                    g_param_spec_object ("screen",
458                                                         P_("Screen"),
459                                                         P_("The associated GdkScreen"),
460                                                         GDK_TYPE_SCREEN,
461                                                         GTK_PARAM_READWRITE));
462   g_object_class_install_property (object_class,
463                                    PROP_DIRECTION,
464                                    g_param_spec_enum ("direction",
465                                                       P_("Direction"),
466                                                       P_("Text direction"),
467                                                       GTK_TYPE_TEXT_DIRECTION,
468                                                       GTK_TEXT_DIR_LTR,
469                                                       GTK_PARAM_READWRITE));
470   /**
471    * GtkStyleContext:parent:
472    *
473    * Sets or gets the style context's parent. See gtk_style_context_set_parent()
474    * for details.
475    *
476    * Since: 3.4
477    */
478   g_object_class_install_property (object_class,
479                                    PROP_PARENT,
480                                    g_param_spec_object ("parent",
481                                                         P_("Parent"),
482                                                         P_("The parent style context"),
483                                                         GTK_TYPE_STYLE_CONTEXT,
484                                                         GTK_PARAM_READWRITE));
485
486   g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
487 }
488
489 static GtkStyleInfo *
490 style_info_new (void)
491 {
492   GtkStyleInfo *info;
493
494   info = g_slice_new0 (GtkStyleInfo);
495   info->style_classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
496   info->regions = g_array_new (FALSE, FALSE, sizeof (GtkRegion));
497
498   return info;
499 }
500
501 static void
502 style_info_free (GtkStyleInfo *info)
503 {
504   g_array_free (info->style_classes, TRUE);
505   g_array_free (info->regions, TRUE);
506   g_slice_free (GtkStyleInfo, info);
507 }
508
509 static GtkStyleInfo *
510 style_info_copy (const GtkStyleInfo *info)
511 {
512   GtkStyleInfo *copy;
513
514   copy = style_info_new ();
515   g_array_insert_vals (copy->style_classes, 0,
516                        info->style_classes->data,
517                        info->style_classes->len);
518
519   g_array_insert_vals (copy->regions, 0,
520                        info->regions->data,
521                        info->regions->len);
522
523   copy->junction_sides = info->junction_sides;
524   copy->state_flags = info->state_flags;
525   copy->data = info->data;
526
527   return copy;
528 }
529
530 static guint
531 style_info_hash (gconstpointer elem)
532 {
533   const GtkStyleInfo *info;
534   guint i, hash = 0;
535
536   info = elem;
537
538   for (i = 0; i < info->style_classes->len; i++)
539     {
540       hash += g_array_index (info->style_classes, GQuark, i);
541       hash <<= 5;
542     }
543
544   for (i = 0; i < info->regions->len; i++)
545     {
546       GtkRegion *region;
547
548       region = &g_array_index (info->regions, GtkRegion, i);
549       hash += region->class_quark;
550       hash += region->flags;
551       hash <<= 5;
552     }
553
554   return hash ^ info->state_flags;
555 }
556
557 static gboolean
558 style_info_equal (gconstpointer elem1,
559                   gconstpointer elem2)
560 {
561   const GtkStyleInfo *info1, *info2;
562
563   info1 = elem1;
564   info2 = elem2;
565
566   if (info1->state_flags != info2->state_flags)
567     return FALSE;
568
569   if (info1->junction_sides != info2->junction_sides)
570     return FALSE;
571
572   if (info1->style_classes->len != info2->style_classes->len)
573     return FALSE;
574
575   if (memcmp (info1->style_classes->data,
576               info2->style_classes->data,
577               info1->style_classes->len * sizeof (GQuark)) != 0)
578     return FALSE;
579
580   if (info1->regions->len != info2->regions->len)
581     return FALSE;
582
583   if (memcmp (info1->regions->data,
584               info2->regions->data,
585               info1->regions->len * sizeof (GtkRegion)) != 0)
586     return FALSE;
587
588   return TRUE;
589 }
590
591 static StyleData *
592 style_data_new (void)
593 {
594   StyleData *data;
595
596   data = g_slice_new0 (StyleData);
597
598   return data;
599 }
600
601 static void
602 clear_property_cache (StyleData *data)
603 {
604   guint i;
605
606   if (!data->property_cache)
607     return;
608
609   for (i = 0; i < data->property_cache->len; i++)
610     {
611       PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);
612
613       g_param_spec_unref (node->pspec);
614       g_value_unset (&node->value);
615     }
616
617   g_array_free (data->property_cache, TRUE);
618   data->property_cache = NULL;
619 }
620
621 static void
622 style_data_free (StyleData *data)
623 {
624   g_object_unref (data->store);
625   clear_property_cache (data);
626
627   g_slice_free (StyleData, data);
628 }
629
630 static void
631 gtk_style_context_init (GtkStyleContext *style_context)
632 {
633   GtkStyleContextPrivate *priv;
634   GtkStyleInfo *info;
635
636   priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
637                                                             GTK_TYPE_STYLE_CONTEXT,
638                                                             GtkStyleContextPrivate);
639
640   priv->style_data = g_hash_table_new_full (style_info_hash,
641                                             style_info_equal,
642                                             (GDestroyNotify) style_info_free,
643                                             (GDestroyNotify) style_data_free);
644   priv->theming_engine = g_object_ref ((gpointer) gtk_theming_engine_load (NULL));
645
646   priv->direction = GTK_TEXT_DIR_LTR;
647
648   priv->screen = gdk_screen_get_default ();
649   priv->cascade = _gtk_style_cascade_get_for_screen (priv->screen);
650   g_object_ref (priv->cascade);
651   priv->relevant_changes = GTK_CSS_CHANGE_ANY;
652
653   /* Create default info store */
654   info = style_info_new ();
655   priv->info_stack = g_slist_prepend (priv->info_stack, info);
656 }
657
658 static void
659 animation_info_free (AnimationInfo *info)
660 {
661   g_object_unref (info->timeline);
662   g_object_unref (info->window);
663
664   if (info->invalidation_region)
665     cairo_region_destroy (info->invalidation_region);
666
667   g_array_free (info->rectangles, TRUE);
668   g_slist_free (info->parent_regions);
669   g_slice_free (AnimationInfo, info);
670 }
671
672 static AnimationInfo *
673 animation_info_lookup_by_timeline (GtkStyleContext *context,
674                                    GtkTimeline     *timeline)
675 {
676   GtkStyleContextPrivate *priv;
677   AnimationInfo *info;
678   GSList *l;
679
680   priv = context->priv;
681
682   for (l = priv->animations; l; l = l->next)
683     {
684       info = l->data;
685
686       if (info->timeline == timeline)
687         return info;
688     }
689
690   return NULL;
691 }
692
693 static void
694 timeline_frame_cb (GtkTimeline *timeline,
695                    gdouble      progress,
696                    gpointer     user_data)
697 {
698   GtkStyleContextPrivate *priv;
699   GtkStyleContext *context;
700   AnimationInfo *info;
701
702   context = user_data;
703   priv = context->priv;
704   info = animation_info_lookup_by_timeline (context, timeline);
705
706   g_assert (info != NULL);
707
708   /* Cancel transition if window is gone */
709   if (gdk_window_is_destroyed (info->window) ||
710       !gdk_window_is_visible (info->window))
711     {
712       priv->animations = g_slist_remove (priv->animations, info);
713       animation_info_free (info);
714       return;
715     }
716
717   if (info->invalidation_region &&
718       !cairo_region_is_empty (info->invalidation_region))
719     gdk_window_invalidate_region (info->window, info->invalidation_region, TRUE);
720   else
721     gdk_window_invalidate_rect (info->window, NULL, TRUE);
722 }
723
724 static void
725 timeline_finished_cb (GtkTimeline *timeline,
726                       gpointer     user_data)
727 {
728   GtkStyleContextPrivate *priv;
729   GtkStyleContext *context;
730   AnimationInfo *info;
731
732   context = user_data;
733   priv = context->priv;
734   info = animation_info_lookup_by_timeline (context, timeline);
735
736   g_assert (info != NULL);
737
738   priv->animations = g_slist_remove (priv->animations, info);
739
740   /* Invalidate one last time the area, so the final content is painted */
741   if (info->invalidation_region &&
742       !cairo_region_is_empty (info->invalidation_region))
743     gdk_window_invalidate_region (info->window, info->invalidation_region, TRUE);
744   else
745     gdk_window_invalidate_rect (info->window, NULL, TRUE);
746
747   animation_info_free (info);
748 }
749
750 static AnimationInfo *
751 animation_info_new (GtkStyleContext         *context,
752                     gpointer                 region_id,
753                     guint                    duration,
754                     GtkTimelineProgressType  progress_type,
755                     gboolean                 loop,
756                     GtkStateType             state,
757                     gboolean                 target_value,
758                     GdkWindow               *window)
759 {
760   AnimationInfo *info;
761
762   info = g_slice_new0 (AnimationInfo);
763
764   info->rectangles = g_array_new (FALSE, FALSE, sizeof (cairo_rectangle_int_t));
765   info->timeline = _gtk_timeline_new (duration);
766   info->window = g_object_ref (window);
767   info->state = state;
768   info->target_value = target_value;
769   info->region_id = region_id;
770
771   _gtk_timeline_set_progress_type (info->timeline, progress_type);
772   _gtk_timeline_set_loop (info->timeline, loop);
773
774   if (!loop && !target_value)
775     {
776       _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
777       _gtk_timeline_rewind (info->timeline);
778     }
779
780   g_signal_connect (info->timeline, "frame",
781                     G_CALLBACK (timeline_frame_cb), context);
782   g_signal_connect (info->timeline, "finished",
783                     G_CALLBACK (timeline_finished_cb), context);
784
785   _gtk_timeline_start (info->timeline);
786
787   return info;
788 }
789
790 static AnimationInfo *
791 animation_info_lookup (GtkStyleContext *context,
792                        gpointer         region_id,
793                        GtkStateType     state)
794 {
795   GtkStyleContextPrivate *priv;
796   GSList *l;
797
798   priv = context->priv;
799
800   for (l = priv->animations; l; l = l->next)
801     {
802       AnimationInfo *info;
803
804       info = l->data;
805
806       if (info->state == state &&
807           info->region_id == region_id)
808         return info;
809     }
810
811   return NULL;
812 }
813
814 static void
815 gtk_style_context_finalize (GObject *object)
816 {
817   GtkStyleContextPrivate *priv;
818   GtkStyleContext *style_context;
819   GSList *l;
820
821   style_context = GTK_STYLE_CONTEXT (object);
822   priv = style_context->priv;
823
824   /* children hold a reference to us */
825   g_assert (priv->children == NULL);
826
827   gtk_style_context_set_parent (style_context, NULL);
828
829   if (priv->widget_path)
830     gtk_widget_path_free (priv->widget_path);
831
832   g_hash_table_destroy (priv->style_data);
833
834   g_object_unref (priv->cascade);
835
836   g_slist_free_full (priv->info_stack, (GDestroyNotify) style_info_free);
837
838   g_slist_free (priv->animation_regions);
839
840   for (l = priv->animations; l; l = l->next)
841     animation_info_free ((AnimationInfo *) l->data);
842
843   g_slist_free (priv->animations);
844
845   if (priv->theming_engine)
846     g_object_unref (priv->theming_engine);
847
848   G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
849 }
850
851 static void
852 gtk_style_context_impl_set_property (GObject      *object,
853                                      guint         prop_id,
854                                      const GValue *value,
855                                      GParamSpec   *pspec)
856 {
857   GtkStyleContext *style_context;
858
859   style_context = GTK_STYLE_CONTEXT (object);
860
861   switch (prop_id)
862     {
863     case PROP_SCREEN:
864       gtk_style_context_set_screen (style_context,
865                                     g_value_get_object (value));
866       break;
867     case PROP_DIRECTION:
868       gtk_style_context_set_direction (style_context,
869                                        g_value_get_enum (value));
870       break;
871     case PROP_PARENT:
872       gtk_style_context_set_parent (style_context,
873                                     g_value_get_object (value));
874       break;
875     default:
876       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
877       break;
878     }
879 }
880
881 static void
882 gtk_style_context_impl_get_property (GObject    *object,
883                                      guint       prop_id,
884                                      GValue     *value,
885                                      GParamSpec *pspec)
886 {
887   GtkStyleContext *style_context;
888   GtkStyleContextPrivate *priv;
889
890   style_context = GTK_STYLE_CONTEXT (object);
891   priv = style_context->priv;
892
893   switch (prop_id)
894     {
895     case PROP_SCREEN:
896       g_value_set_object (value, priv->screen);
897       break;
898     case PROP_DIRECTION:
899       g_value_set_enum (value, priv->direction);
900       break;
901     case PROP_PARENT:
902       g_value_set_object (value, priv->parent);
903       break;
904     default:
905       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
906       break;
907     }
908 }
909
910 static void
911 build_properties (GtkStyleContext *context,
912                   StyleData       *style_data,
913                   GtkWidgetPath   *path,
914                   GtkStateFlags    state)
915 {
916   GtkStyleContextPrivate *priv;
917   GtkCssMatcher matcher;
918   GtkCssLookup *lookup;
919
920   priv = context->priv;
921
922   _gtk_css_matcher_init (&matcher, path, state);
923   lookup = _gtk_css_lookup_new ();
924
925   _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
926                                       &matcher,
927                                       lookup);
928
929   style_data->store = _gtk_css_computed_values_new ();
930   _gtk_css_lookup_resolve (lookup, context, style_data->store);
931   _gtk_css_lookup_free (lookup);
932 }
933
934 static GtkWidgetPath *
935 create_query_path (GtkStyleContext *context)
936 {
937   GtkStyleContextPrivate *priv;
938   GtkWidgetPath *path;
939   GtkStyleInfo *info;
940   guint i, pos;
941
942   priv = context->priv;
943   path = gtk_widget_path_copy (priv->widget ? gtk_widget_get_path (priv->widget) : priv->widget_path);
944   pos = gtk_widget_path_length (path) - 1;
945
946   info = priv->info_stack->data;
947
948   /* Set widget regions */
949   for (i = 0; i < info->regions->len; i++)
950     {
951       GtkRegion *region;
952
953       region = &g_array_index (info->regions, GtkRegion, i);
954       gtk_widget_path_iter_add_region (path, pos,
955                                        g_quark_to_string (region->class_quark),
956                                        region->flags);
957     }
958
959   /* Set widget classes */
960   for (i = 0; i < info->style_classes->len; i++)
961     {
962       GQuark quark;
963
964       quark = g_array_index (info->style_classes, GQuark, i);
965       gtk_widget_path_iter_add_class (path, pos,
966                                       g_quark_to_string (quark));
967     }
968
969   return path;
970 }
971
972 static StyleData *
973 style_data_lookup (GtkStyleContext *context)
974 {
975   GtkStyleContextPrivate *priv;
976   GtkStyleInfo *info;
977   GtkCssValue *v;
978
979   priv = context->priv;
980   info = priv->info_stack->data;
981
982   /* Current data in use is cached, just return it */
983   if (info->data)
984     return info->data;
985
986   g_assert (priv->widget != NULL || priv->widget_path != NULL);
987
988   info->data = g_hash_table_lookup (priv->style_data, info);
989
990   if (!info->data)
991     {
992       GtkWidgetPath *path;
993
994       path = create_query_path (context);
995
996       info->data = style_data_new ();
997       g_hash_table_insert (priv->style_data,
998                            style_info_copy (info),
999                            info->data);
1000
1001       build_properties (context, info->data, path, info->state_flags);
1002
1003       gtk_widget_path_free (path);
1004     }
1005
1006   if (priv->theming_engine)
1007     g_object_unref (priv->theming_engine);
1008
1009   v = _gtk_css_computed_values_get_value_by_name (info->data->store, "engine");
1010   if (v)
1011     priv->theming_engine = _gtk_css_value_dup_object (v);
1012   else
1013     priv->theming_engine = g_object_ref (gtk_theming_engine_load (NULL));
1014
1015   return info->data;
1016 }
1017
1018 static void
1019 gtk_style_context_set_invalid (GtkStyleContext *context,
1020                                gboolean         invalid)
1021 {
1022   GtkStyleContextPrivate *priv;
1023   
1024   priv = context->priv;
1025
1026   if (priv->invalid == invalid)
1027     return;
1028
1029   priv->invalid = invalid;
1030
1031   if (invalid)
1032     {
1033       if (priv->widget)
1034         gtk_widget_queue_resize (priv->widget);
1035       if (priv->parent)
1036         gtk_style_context_set_invalid (priv->parent, TRUE);
1037     }
1038 }
1039
1040 /* returns TRUE if someone called gtk_style_context_save() but hasn't
1041  * called gtk_style_context_restore() yet.
1042  * In those situations we don't invalidate the context when somebody
1043  * changes state/regions/classes.
1044  */
1045 static gboolean
1046 gtk_style_context_is_saved (GtkStyleContext *context)
1047 {
1048   return context->priv->info_stack->next != NULL;
1049 }
1050
1051 static void
1052 gtk_style_context_queue_invalidate_internal (GtkStyleContext *context,
1053                                              GtkCssChange     change)
1054 {
1055   GtkStyleContextPrivate *priv = context->priv;
1056   GtkStyleInfo *info = priv->info_stack->data;
1057
1058   if (gtk_style_context_is_saved (context))
1059     {
1060       info->data = NULL;
1061     }
1062   else
1063     {
1064       _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_STATE);
1065       /* XXX: We need to invalidate siblings here somehow */
1066     }
1067 }
1068
1069 /**
1070  * gtk_style_context_new:
1071  *
1072  * Creates a standalone #GtkStyleContext, this style context
1073  * won't be attached to any widget, so you may want
1074  * to call gtk_style_context_set_path() yourself.
1075  *
1076  * <note>
1077  * This function is only useful when using the theming layer
1078  * separated from GTK+, if you are using #GtkStyleContext to
1079  * theme #GtkWidget<!-- -->s, use gtk_widget_get_style_context()
1080  * in order to get a style context ready to theme the widget.
1081  * </note>
1082  *
1083  * Returns: A newly created #GtkStyleContext.
1084  **/
1085 GtkStyleContext *
1086 gtk_style_context_new (void)
1087 {
1088   return g_object_new (GTK_TYPE_STYLE_CONTEXT, NULL);
1089 }
1090
1091 void
1092 _gtk_style_context_set_widget (GtkStyleContext *context,
1093                                GtkWidget       *widget)
1094 {
1095   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1096   g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
1097
1098   context->priv->widget = widget;
1099
1100   _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
1101 }
1102
1103 /**
1104  * gtk_style_context_add_provider:
1105  * @context: a #GtkStyleContext
1106  * @provider: a #GtkStyleProvider
1107  * @priority: the priority of the style provider. The lower
1108  *            it is, the earlier it will be used in the style
1109  *            construction. Typically this will be in the range
1110  *            between %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and
1111  *            %GTK_STYLE_PROVIDER_PRIORITY_USER
1112  *
1113  * Adds a style provider to @context, to be used in style construction.
1114  *
1115  * <note><para>If both priorities are the same, A #GtkStyleProvider
1116  * added through this function takes precedence over another added
1117  * through gtk_style_context_add_provider_for_screen().</para></note>
1118  *
1119  * Since: 3.0
1120  **/
1121 void
1122 gtk_style_context_add_provider (GtkStyleContext  *context,
1123                                 GtkStyleProvider *provider,
1124                                 guint             priority)
1125 {
1126   GtkStyleContextPrivate *priv;
1127
1128   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1129   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1130
1131   priv = context->priv;
1132
1133   if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
1134     {
1135       GtkStyleCascade *new_cascade;
1136       
1137       new_cascade = _gtk_style_cascade_new ();
1138       _gtk_style_cascade_set_parent (new_cascade, priv->cascade);
1139       g_object_unref (priv->cascade);
1140       priv->cascade = new_cascade;
1141     }
1142
1143   _gtk_style_cascade_add_provider (priv->cascade, provider, priority);
1144
1145   gtk_style_context_invalidate (context);
1146 }
1147
1148 /**
1149  * gtk_style_context_remove_provider:
1150  * @context: a #GtkStyleContext
1151  * @provider: a #GtkStyleProvider
1152  *
1153  * Removes @provider from the style providers list in @context.
1154  *
1155  * Since: 3.0
1156  **/
1157 void
1158 gtk_style_context_remove_provider (GtkStyleContext  *context,
1159                                    GtkStyleProvider *provider)
1160 {
1161   GtkStyleContextPrivate *priv;
1162
1163   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1164   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1165
1166   priv = context->priv;
1167
1168   if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
1169     return;
1170
1171   _gtk_style_cascade_remove_provider (priv->cascade, provider);
1172 }
1173
1174 /**
1175  * gtk_style_context_reset_widgets:
1176  * @screen: a #GdkScreen
1177  *
1178  * This function recomputes the styles for all widgets under a particular
1179  * #GdkScreen. This is useful when some global parameter has changed that
1180  * affects the appearance of all widgets, because when a widget gets a new
1181  * style, it will both redraw and recompute any cached information about
1182  * its appearance. As an example, it is used when the color scheme changes
1183  * in the related #GtkSettings object.
1184  *
1185  * Since: 3.0
1186  **/
1187 void
1188 gtk_style_context_reset_widgets (GdkScreen *screen)
1189 {
1190   GList *list, *toplevels;
1191
1192   _gtk_icon_set_invalidate_caches ();
1193
1194   toplevels = gtk_window_list_toplevels ();
1195   g_list_foreach (toplevels, (GFunc) g_object_ref, NULL);
1196
1197   for (list = toplevels; list; list = list->next)
1198     {
1199       if (gtk_widget_get_screen (list->data) == screen)
1200         gtk_widget_reset_style (list->data);
1201
1202       g_object_unref (list->data);
1203     }
1204
1205   g_list_free (toplevels);
1206 }
1207
1208 /**
1209  * gtk_style_context_add_provider_for_screen:
1210  * @screen: a #GdkScreen
1211  * @provider: a #GtkStyleProvider
1212  * @priority: the priority of the style provider. The lower
1213  *            it is, the earlier it will be used in the style
1214  *            construction. Typically this will be in the range
1215  *            between %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and
1216  *            %GTK_STYLE_PROVIDER_PRIORITY_USER
1217  *
1218  * Adds a global style provider to @screen, which will be used
1219  * in style construction for all #GtkStyleContext<!-- -->s under
1220  * @screen.
1221  *
1222  * GTK+ uses this to make styling information from #GtkSettings
1223  * available.
1224  *
1225  * <note><para>If both priorities are the same, A #GtkStyleProvider
1226  * added through gtk_style_context_add_provider() takes precedence
1227  * over another added through this function.</para></note>
1228  *
1229  * Since: 3.0
1230  **/
1231 void
1232 gtk_style_context_add_provider_for_screen (GdkScreen        *screen,
1233                                            GtkStyleProvider *provider,
1234                                            guint             priority)
1235 {
1236   GtkStyleCascade *cascade;
1237
1238   g_return_if_fail (GDK_IS_SCREEN (screen));
1239   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1240
1241   cascade = _gtk_style_cascade_get_for_screen (screen);
1242   _gtk_style_cascade_add_provider (cascade, provider, priority);
1243 }
1244
1245 /**
1246  * gtk_style_context_remove_provider_for_screen:
1247  * @screen: a #GdkScreen
1248  * @provider: a #GtkStyleProvider
1249  *
1250  * Removes @provider from the global style providers list in @screen.
1251  *
1252  * Since: 3.0
1253  **/
1254 void
1255 gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
1256                                               GtkStyleProvider *provider)
1257 {
1258   GtkStyleCascade *cascade;
1259
1260   g_return_if_fail (GDK_IS_SCREEN (screen));
1261   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1262
1263   cascade = _gtk_style_cascade_get_for_screen (screen);
1264   _gtk_style_cascade_remove_provider (cascade, provider);
1265 }
1266
1267 /**
1268  * gtk_style_context_get_section:
1269  * @context: a #GtkStyleContext
1270  * @property: style property name
1271  *
1272  * Queries the location in the CSS where @property was defined for the
1273  * current @context. Note that the state to be queried is taken from
1274  * gtk_style_context_get_state().
1275  *
1276  * If the location is not available, %NULL will be returned. The
1277  * location might not be available for various reasons, such as the
1278  * property being overridden, @property not naming a supported CSS
1279  * property or tracking of definitions being disabled for performance
1280  * reasons.
1281  *
1282  * Shorthand CSS properties cannot be queried for a location and will
1283  * always return %NULL.
1284  *
1285  * Returns: %NULL or the section where value was defined
1286  **/
1287 GtkCssSection *
1288 gtk_style_context_get_section (GtkStyleContext *context,
1289                                const gchar     *property)
1290 {
1291   GtkStyleContextPrivate *priv;
1292   GtkStyleProperty *prop;
1293   StyleData *data;
1294
1295   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
1296   g_return_val_if_fail (property != NULL, NULL);
1297
1298   priv = context->priv;
1299   g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
1300
1301   prop = _gtk_style_property_lookup (property);
1302   if (!GTK_IS_CSS_STYLE_PROPERTY (prop))
1303     return NULL;
1304
1305   data = style_data_lookup (context);
1306   return _gtk_css_computed_values_get_section (data->store, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)));
1307 }
1308
1309 static GtkCssValue *
1310 gtk_style_context_query_func (guint    id,
1311                               gpointer values)
1312 {
1313   return _gtk_css_computed_values_get_value (values, id);
1314 }
1315
1316 /**
1317  * gtk_style_context_get_property:
1318  * @context: a #GtkStyleContext
1319  * @property: style property name
1320  * @state: state to retrieve the property value for
1321  * @value: (out) (transfer full):  return location for the style property value
1322  *
1323  * Gets a style property from @context for the given state.
1324  *
1325  * When @value is no longer needed, g_value_unset() must be called
1326  * to free any allocated memory.
1327  *
1328  * Since: 3.0
1329  **/
1330 void
1331 gtk_style_context_get_property (GtkStyleContext *context,
1332                                 const gchar     *property,
1333                                 GtkStateFlags    state,
1334                                 GValue          *value)
1335 {
1336   GtkStyleContextPrivate *priv;
1337   GtkStyleProperty *prop;
1338   StyleData *data;
1339
1340   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1341   g_return_if_fail (property != NULL);
1342   g_return_if_fail (value != NULL);
1343
1344   priv = context->priv;
1345   g_return_if_fail (priv->widget != NULL || priv->widget_path != NULL);
1346
1347   prop = _gtk_style_property_lookup (property);
1348   if (prop == NULL)
1349     {
1350       g_warning ("Style property \"%s\" is not registered", property);
1351       return;
1352     }
1353   if (_gtk_style_property_get_value_type (prop) == G_TYPE_NONE)
1354     {
1355       g_warning ("Style property \"%s\" is not gettable", property);
1356       return;
1357     }
1358
1359   gtk_style_context_save (context);
1360   gtk_style_context_set_state (context, state);
1361   data = style_data_lookup (context);
1362   _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
1363   gtk_style_context_restore (context);
1364 }
1365
1366 /**
1367  * gtk_style_context_get_valist:
1368  * @context: a #GtkStyleContext
1369  * @state: state to retrieve the property values for
1370  * @args: va_list of property name/return location pairs, followed by %NULL
1371  *
1372  * Retrieves several style property values from @context for a given state.
1373  *
1374  * Since: 3.0
1375  **/
1376 void
1377 gtk_style_context_get_valist (GtkStyleContext *context,
1378                               GtkStateFlags    state,
1379                               va_list          args)
1380 {
1381   const gchar *property_name;
1382
1383   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1384
1385   property_name = va_arg (args, const gchar *);
1386
1387   while (property_name)
1388     {
1389       gchar *error = NULL;
1390       GValue value = G_VALUE_INIT;
1391
1392       gtk_style_context_get_property (context,
1393                                       property_name,
1394                                       state,
1395                                       &value);
1396
1397       G_VALUE_LCOPY (&value, args, 0, &error);
1398       g_value_unset (&value);
1399
1400       if (error)
1401         {
1402           g_warning ("Could not get style property \"%s\": %s", property_name, error);
1403           g_free (error);
1404           break;
1405         }
1406
1407       property_name = va_arg (args, const gchar *);
1408     }
1409 }
1410
1411 /**
1412  * gtk_style_context_get:
1413  * @context: a #GtkStyleContext
1414  * @state: state to retrieve the property values for
1415  * @...: property name /return value pairs, followed by %NULL
1416  *
1417  * Retrieves several style property values from @context for a
1418  * given state.
1419  *
1420  * Since: 3.0
1421  **/
1422 void
1423 gtk_style_context_get (GtkStyleContext *context,
1424                        GtkStateFlags    state,
1425                        ...)
1426 {
1427   va_list args;
1428
1429   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1430
1431   va_start (args, state);
1432   gtk_style_context_get_valist (context, state, args);
1433   va_end (args);
1434 }
1435
1436 /**
1437  * gtk_style_context_set_state:
1438  * @context: a #GtkStyleContext
1439  * @flags: state to represent
1440  *
1441  * Sets the state to be used when rendering with any
1442  * of the gtk_render_*() functions.
1443  *
1444  * Since: 3.0
1445  **/
1446 void
1447 gtk_style_context_set_state (GtkStyleContext *context,
1448                              GtkStateFlags    flags)
1449 {
1450   GtkStyleContextPrivate *priv;
1451   GtkStyleInfo *info;
1452
1453   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1454
1455   priv = context->priv;
1456   info = priv->info_stack->data;
1457   info->state_flags = flags;
1458   
1459   gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
1460 }
1461
1462 /**
1463  * gtk_style_context_get_state:
1464  * @context: a #GtkStyleContext
1465  *
1466  * Returns the state used when rendering.
1467  *
1468  * Returns: the state flags
1469  *
1470  * Since: 3.0
1471  **/
1472 GtkStateFlags
1473 gtk_style_context_get_state (GtkStyleContext *context)
1474 {
1475   GtkStyleContextPrivate *priv;
1476   GtkStyleInfo *info;
1477
1478   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
1479
1480   priv = context->priv;
1481   info = priv->info_stack->data;
1482
1483   return info->state_flags;
1484 }
1485
1486 static gboolean
1487 context_has_animatable_region (GtkStyleContext *context,
1488                                gpointer         region_id)
1489 {
1490   GtkStyleContextPrivate *priv;
1491
1492   /* NULL region_id means everything
1493    * rendered through the style context
1494    */
1495   if (!region_id)
1496     return TRUE;
1497
1498   priv = context->priv;
1499   return g_slist_find (priv->animation_regions, region_id) != NULL;
1500 }
1501
1502 /**
1503  * gtk_style_context_state_is_running:
1504  * @context: a #GtkStyleContext
1505  * @state: a widget state
1506  * @progress: (out): return location for the transition progress
1507  *
1508  * Returns %TRUE if there is a transition animation running for the
1509  * current region (see gtk_style_context_push_animatable_region()).
1510  *
1511  * If @progress is not %NULL, the animation progress will be returned
1512  * there, 0.0 means the state is closest to being unset, while 1.0 means
1513  * it's closest to being set. This means transition animation will
1514  * run from 0 to 1 when @state is being set and from 1 to 0 when
1515  * it's being unset.
1516  *
1517  * Returns: %TRUE if there is a running transition animation for @state.
1518  *
1519  * Since: 3.0
1520  **/
1521 gboolean
1522 gtk_style_context_state_is_running (GtkStyleContext *context,
1523                                     GtkStateType     state,
1524                                     gdouble         *progress)
1525 {
1526   GtkStyleContextPrivate *priv;
1527   AnimationInfo *info;
1528   GSList *l;
1529
1530   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
1531
1532   priv = context->priv;
1533
1534   for (l = priv->animations; l; l = l->next)
1535     {
1536       info = l->data;
1537
1538       if (info->state == state &&
1539           context_has_animatable_region (context, info->region_id))
1540         {
1541           if (progress)
1542             *progress = _gtk_timeline_get_progress (info->timeline);
1543
1544           return TRUE;
1545         }
1546     }
1547
1548   return FALSE;
1549 }
1550
1551 /**
1552  * gtk_style_context_set_path:
1553  * @context: a #GtkStyleContext
1554  * @path: a #GtkWidgetPath
1555  *
1556  * Sets the #GtkWidgetPath used for style matching. As a
1557  * consequence, the style will be regenerated to match
1558  * the new given path.
1559  *
1560  * If you are using a #GtkStyleContext returned from
1561  * gtk_widget_get_style_context(), you do not need to call
1562  * this yourself.
1563  *
1564  * Since: 3.0
1565  **/
1566 void
1567 gtk_style_context_set_path (GtkStyleContext *context,
1568                             GtkWidgetPath   *path)
1569 {
1570   GtkStyleContextPrivate *priv;
1571
1572   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1573   g_return_if_fail (path != NULL);
1574
1575   priv = context->priv;
1576   g_return_if_fail (priv->widget == NULL);
1577
1578   if (priv->widget_path)
1579     {
1580       gtk_widget_path_free (priv->widget_path);
1581       priv->widget_path = NULL;
1582     }
1583
1584   if (path)
1585     priv->widget_path = gtk_widget_path_copy (path);
1586
1587   _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY);
1588 }
1589
1590 /**
1591  * gtk_style_context_get_path:
1592  * @context: a #GtkStyleContext
1593  *
1594  * Returns the widget path used for style matching.
1595  *
1596  * Returns: (transfer none): A #GtkWidgetPath
1597  *
1598  * Since: 3.0
1599  **/
1600 const GtkWidgetPath *
1601 gtk_style_context_get_path (GtkStyleContext *context)
1602 {
1603   GtkStyleContextPrivate *priv;
1604
1605   priv = context->priv;
1606   if (priv->widget)
1607     return gtk_widget_get_path (priv->widget);
1608   else
1609     return priv->widget_path;
1610 }
1611
1612 /**
1613  * gtk_style_context_set_parent:
1614  * @context: a #GtkStyleContext
1615  * @parent: (allow-none): the new parent or %NULL
1616  *
1617  * Sets the parent style context for @context. The parent style
1618  * context is used to implement
1619  * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance">inheritance</ulink>
1620  * of properties.
1621  *
1622  * If you are using a #GtkStyleContext returned from
1623  * gtk_widget_get_style_context(), the parent will be set for you.
1624  *
1625  * Since: 3.4
1626  **/
1627 void
1628 gtk_style_context_set_parent (GtkStyleContext *context,
1629                               GtkStyleContext *parent)
1630 {
1631   GtkStyleContextPrivate *priv;
1632
1633   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1634   g_return_if_fail (parent == NULL || GTK_IS_STYLE_CONTEXT (parent));
1635
1636   priv = context->priv;
1637
1638   if (priv->parent == parent)
1639     return;
1640
1641   if (parent)
1642     {
1643       parent->priv->children = g_slist_prepend (parent->priv->children, context);
1644       g_object_ref (parent);
1645       if (priv->invalid)
1646         gtk_style_context_set_invalid (parent, TRUE);
1647     }
1648
1649   if (priv->parent)
1650     {
1651       priv->parent->priv->children = g_slist_remove (priv->parent->priv->children, context);
1652       g_object_unref (priv->parent);
1653     }
1654
1655   priv->parent = parent;
1656
1657   g_object_notify (G_OBJECT (context), "parent");
1658   _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_PARENT | GTK_CSS_CHANGE_ANY_SIBLING);
1659 }
1660
1661 /**
1662  * gtk_style_context_get_parent:
1663  * @context: a #GtkStyleContext
1664  *
1665  * Gets the parent context set via gtk_style_context_set_parent().
1666  * See that function for details.
1667  *
1668  * Returns: (transfer none): the parent context or %NULL
1669  *
1670  * Since: 3.4
1671  **/
1672 GtkStyleContext *
1673 gtk_style_context_get_parent (GtkStyleContext *context)
1674 {
1675   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
1676
1677   return context->priv->parent;
1678 }
1679
1680 /**
1681  * gtk_style_context_save:
1682  * @context: a #GtkStyleContext
1683  *
1684  * Saves the @context state, so all modifications done through
1685  * gtk_style_context_add_class(), gtk_style_context_remove_class(),
1686  * gtk_style_context_add_region(), gtk_style_context_remove_region()
1687  * or gtk_style_context_set_junction_sides() can be reverted in one
1688  * go through gtk_style_context_restore().
1689  *
1690  * Since: 3.0
1691  **/
1692 void
1693 gtk_style_context_save (GtkStyleContext *context)
1694 {
1695   GtkStyleContextPrivate *priv;
1696   GtkStyleInfo *info;
1697
1698   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1699
1700   priv = context->priv;
1701
1702   g_assert (priv->info_stack != NULL);
1703
1704   info = style_info_copy (priv->info_stack->data);
1705   priv->info_stack = g_slist_prepend (priv->info_stack, info);
1706 }
1707
1708 /**
1709  * gtk_style_context_restore:
1710  * @context: a #GtkStyleContext
1711  *
1712  * Restores @context state to a previous stage.
1713  * See gtk_style_context_save().
1714  *
1715  * Since: 3.0
1716  **/
1717 void
1718 gtk_style_context_restore (GtkStyleContext *context)
1719 {
1720   GtkStyleContextPrivate *priv;
1721   GtkStyleInfo *info;
1722
1723   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1724
1725   priv = context->priv;
1726
1727   if (priv->info_stack)
1728     {
1729       info = priv->info_stack->data;
1730       priv->info_stack = g_slist_remove (priv->info_stack, info);
1731       style_info_free (info);
1732     }
1733
1734   if (!priv->info_stack)
1735     {
1736       g_warning ("Unpaired gtk_style_context_restore() call");
1737
1738       /* Create default region */
1739       info = style_info_new ();
1740       priv->info_stack = g_slist_prepend (priv->info_stack, info);
1741     }
1742 }
1743
1744 static gboolean
1745 style_class_find (GArray *array,
1746                   GQuark  class_quark,
1747                   guint  *position)
1748 {
1749   gint min, max, mid;
1750   gboolean found = FALSE;
1751   guint pos;
1752
1753   if (position)
1754     *position = 0;
1755
1756   if (!array || array->len == 0)
1757     return FALSE;
1758
1759   min = 0;
1760   max = array->len - 1;
1761
1762   do
1763     {
1764       GQuark item;
1765
1766       mid = (min + max) / 2;
1767       item = g_array_index (array, GQuark, mid);
1768
1769       if (class_quark == item)
1770         {
1771           found = TRUE;
1772           pos = mid;
1773         }
1774       else if (class_quark > item)
1775         min = pos = mid + 1;
1776       else
1777         {
1778           max = mid - 1;
1779           pos = mid;
1780         }
1781     }
1782   while (!found && min <= max);
1783
1784   if (position)
1785     *position = pos;
1786
1787   return found;
1788 }
1789
1790 static gboolean
1791 region_find (GArray *array,
1792              GQuark  class_quark,
1793              guint  *position)
1794 {
1795   gint min, max, mid;
1796   gboolean found = FALSE;
1797   guint pos;
1798
1799   if (position)
1800     *position = 0;
1801
1802   if (!array || array->len == 0)
1803     return FALSE;
1804
1805   min = 0;
1806   max = array->len - 1;
1807
1808   do
1809     {
1810       GtkRegion *region;
1811
1812       mid = (min + max) / 2;
1813       region = &g_array_index (array, GtkRegion, mid);
1814
1815       if (region->class_quark == class_quark)
1816         {
1817           found = TRUE;
1818           pos = mid;
1819         }
1820       else if (region->class_quark > class_quark)
1821         min = pos = mid + 1;
1822       else
1823         {
1824           max = mid - 1;
1825           pos = mid;
1826         }
1827     }
1828   while (!found && min <= max);
1829
1830   if (position)
1831     *position = pos;
1832
1833   return found;
1834 }
1835
1836 /**
1837  * gtk_style_context_add_class:
1838  * @context: a #GtkStyleContext
1839  * @class_name: class name to use in styling
1840  *
1841  * Adds a style class to @context, so posterior calls to
1842  * gtk_style_context_get() or any of the gtk_render_*()
1843  * functions will make use of this new class for styling.
1844  *
1845  * In the CSS file format, a #GtkEntry defining an "entry"
1846  * class, would be matched by:
1847  *
1848  * <programlisting>
1849  * GtkEntry.entry { ... }
1850  * </programlisting>
1851  *
1852  * While any widget defining an "entry" class would be
1853  * matched by:
1854  * <programlisting>
1855  * .entry { ... }
1856  * </programlisting>
1857  *
1858  * Since: 3.0
1859  **/
1860 void
1861 gtk_style_context_add_class (GtkStyleContext *context,
1862                              const gchar     *class_name)
1863 {
1864   GtkStyleContextPrivate *priv;
1865   GtkStyleInfo *info;
1866   GQuark class_quark;
1867   guint position;
1868
1869   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1870   g_return_if_fail (class_name != NULL);
1871
1872   priv = context->priv;
1873   class_quark = g_quark_from_string (class_name);
1874
1875   g_assert (priv->info_stack != NULL);
1876   info = priv->info_stack->data;
1877
1878   if (!style_class_find (info->style_classes, class_quark, &position))
1879     {
1880       g_array_insert_val (info->style_classes, position, class_quark);
1881
1882       gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_CLASS);
1883     }
1884 }
1885
1886 /**
1887  * gtk_style_context_remove_class:
1888  * @context: a #GtkStyleContext
1889  * @class_name: class name to remove
1890  *
1891  * Removes @class_name from @context.
1892  *
1893  * Since: 3.0
1894  **/
1895 void
1896 gtk_style_context_remove_class (GtkStyleContext *context,
1897                                 const gchar     *class_name)
1898 {
1899   GtkStyleContextPrivate *priv;
1900   GtkStyleInfo *info;
1901   GQuark class_quark;
1902   guint position;
1903
1904   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1905   g_return_if_fail (class_name != NULL);
1906
1907   class_quark = g_quark_try_string (class_name);
1908
1909   if (!class_quark)
1910     return;
1911
1912   priv = context->priv;
1913
1914   g_assert (priv->info_stack != NULL);
1915   info = priv->info_stack->data;
1916
1917   if (style_class_find (info->style_classes, class_quark, &position))
1918     {
1919       g_array_remove_index (info->style_classes, position);
1920
1921       gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_CLASS);
1922     }
1923 }
1924
1925 /**
1926  * gtk_style_context_has_class:
1927  * @context: a #GtkStyleContext
1928  * @class_name: a class name
1929  *
1930  * Returns %TRUE if @context currently has defined the
1931  * given class name
1932  *
1933  * Returns: %TRUE if @context has @class_name defined
1934  *
1935  * Since: 3.0
1936  **/
1937 gboolean
1938 gtk_style_context_has_class (GtkStyleContext *context,
1939                              const gchar     *class_name)
1940 {
1941   GtkStyleContextPrivate *priv;
1942   GtkStyleInfo *info;
1943   GQuark class_quark;
1944
1945   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
1946   g_return_val_if_fail (class_name != NULL, FALSE);
1947
1948   class_quark = g_quark_try_string (class_name);
1949
1950   if (!class_quark)
1951     return FALSE;
1952
1953   priv = context->priv;
1954
1955   g_assert (priv->info_stack != NULL);
1956   info = priv->info_stack->data;
1957
1958   if (style_class_find (info->style_classes, class_quark, NULL))
1959     return TRUE;
1960
1961   return FALSE;
1962 }
1963
1964 /**
1965  * gtk_style_context_list_classes:
1966  * @context: a #GtkStyleContext
1967  *
1968  * Returns the list of classes currently defined in @context.
1969  *
1970  * Returns: (transfer container) (element-type utf8): a #GList of
1971  *          strings with the currently defined classes. The contents
1972  *          of the list are owned by GTK+, but you must free the list
1973  *          itself with g_list_free() when you are done with it.
1974  *
1975  * Since: 3.0
1976  **/
1977 GList *
1978 gtk_style_context_list_classes (GtkStyleContext *context)
1979 {
1980   GtkStyleContextPrivate *priv;
1981   GtkStyleInfo *info;
1982   GList *classes = NULL;
1983   guint i;
1984
1985   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
1986
1987   priv = context->priv;
1988
1989   g_assert (priv->info_stack != NULL);
1990   info = priv->info_stack->data;
1991
1992   for (i = 0; i < info->style_classes->len; i++)
1993     {
1994       GQuark quark;
1995
1996       quark = g_array_index (info->style_classes, GQuark, i);
1997       classes = g_list_prepend (classes, (gchar *) g_quark_to_string (quark));
1998     }
1999
2000   return classes;
2001 }
2002
2003 /**
2004  * gtk_style_context_list_regions:
2005  * @context: a #GtkStyleContext
2006  *
2007  * Returns the list of regions currently defined in @context.
2008  *
2009  * Returns: (transfer container) (element-type utf8): a #GList of
2010  *          strings with the currently defined regions. The contents
2011  *          of the list are owned by GTK+, but you must free the list
2012  *          itself with g_list_free() when you are done with it.
2013  *
2014  * Since: 3.0
2015  **/
2016 GList *
2017 gtk_style_context_list_regions (GtkStyleContext *context)
2018 {
2019   GtkStyleContextPrivate *priv;
2020   GtkStyleInfo *info;
2021   GList *classes = NULL;
2022   guint i;
2023
2024   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
2025
2026   priv = context->priv;
2027
2028   g_assert (priv->info_stack != NULL);
2029   info = priv->info_stack->data;
2030
2031   for (i = 0; i < info->regions->len; i++)
2032     {
2033       GtkRegion *region;
2034       const gchar *class_name;
2035
2036       region = &g_array_index (info->regions, GtkRegion, i);
2037
2038       class_name = g_quark_to_string (region->class_quark);
2039       classes = g_list_prepend (classes, (gchar *) class_name);
2040     }
2041
2042   return classes;
2043 }
2044
2045 gboolean
2046 _gtk_style_context_check_region_name (const gchar *str)
2047 {
2048   g_return_val_if_fail (str != NULL, FALSE);
2049
2050   if (!g_ascii_islower (str[0]))
2051     return FALSE;
2052
2053   while (*str)
2054     {
2055       if (*str != '-' &&
2056           !g_ascii_islower (*str))
2057         return FALSE;
2058
2059       str++;
2060     }
2061
2062   return TRUE;
2063 }
2064
2065 /**
2066  * gtk_style_context_add_region:
2067  * @context: a #GtkStyleContext
2068  * @region_name: region name to use in styling
2069  * @flags: flags that apply to the region
2070  *
2071  * Adds a region to @context, so posterior calls to
2072  * gtk_style_context_get() or any of the gtk_render_*()
2073  * functions will make use of this new region for styling.
2074  *
2075  * In the CSS file format, a #GtkTreeView defining a "row"
2076  * region, would be matched by:
2077  *
2078  * <programlisting>
2079  * GtkTreeView row { ... }
2080  * </programlisting>
2081  *
2082  * Pseudo-classes are used for matching @flags, so the two
2083  * following rules:
2084  * <programlisting>
2085  * GtkTreeView row:nth-child(even) { ... }
2086  * GtkTreeView row:nth-child(odd) { ... }
2087  * </programlisting>
2088  *
2089  * would apply to even and odd rows, respectively.
2090  *
2091  * <note><para>Region names must only contain lowercase letters
2092  * and '-', starting always with a lowercase letter.</para></note>
2093  *
2094  * Since: 3.0
2095  **/
2096 void
2097 gtk_style_context_add_region (GtkStyleContext *context,
2098                               const gchar     *region_name,
2099                               GtkRegionFlags   flags)
2100 {
2101   GtkStyleContextPrivate *priv;
2102   GtkStyleInfo *info;
2103   GQuark region_quark;
2104   guint position;
2105
2106   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2107   g_return_if_fail (region_name != NULL);
2108   g_return_if_fail (_gtk_style_context_check_region_name (region_name));
2109
2110   priv = context->priv;
2111   region_quark = g_quark_from_string (region_name);
2112
2113   g_assert (priv->info_stack != NULL);
2114   info = priv->info_stack->data;
2115
2116   if (!region_find (info->regions, region_quark, &position))
2117     {
2118       GtkRegion region;
2119
2120       region.class_quark = region_quark;
2121       region.flags = flags;
2122
2123       g_array_insert_val (info->regions, position, region);
2124
2125       gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_REGION);
2126     }
2127 }
2128
2129 /**
2130  * gtk_style_context_remove_region:
2131  * @context: a #GtkStyleContext
2132  * @region_name: region name to unset
2133  *
2134  * Removes a region from @context.
2135  *
2136  * Since: 3.0
2137  **/
2138 void
2139 gtk_style_context_remove_region (GtkStyleContext *context,
2140                                  const gchar     *region_name)
2141 {
2142   GtkStyleContextPrivate *priv;
2143   GtkStyleInfo *info;
2144   GQuark region_quark;
2145   guint position;
2146
2147   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2148   g_return_if_fail (region_name != NULL);
2149
2150   region_quark = g_quark_try_string (region_name);
2151
2152   if (!region_quark)
2153     return;
2154
2155   priv = context->priv;
2156
2157   g_assert (priv->info_stack != NULL);
2158   info = priv->info_stack->data;
2159
2160   if (region_find (info->regions, region_quark, &position))
2161     {
2162       g_array_remove_index (info->regions, position);
2163
2164       gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_REGION);
2165     }
2166 }
2167
2168 /**
2169  * gtk_style_context_has_region:
2170  * @context: a #GtkStyleContext
2171  * @region_name: a region name
2172  * @flags_return: (out) (allow-none): return location for region flags
2173  *
2174  * Returns %TRUE if @context has the region defined.
2175  * If @flags_return is not %NULL, it is set to the flags
2176  * affecting the region.
2177  *
2178  * Returns: %TRUE if region is defined
2179  *
2180  * Since: 3.0
2181  **/
2182 gboolean
2183 gtk_style_context_has_region (GtkStyleContext *context,
2184                               const gchar     *region_name,
2185                               GtkRegionFlags  *flags_return)
2186 {
2187   GtkStyleContextPrivate *priv;
2188   GtkStyleInfo *info;
2189   GQuark region_quark;
2190   guint position;
2191
2192   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
2193   g_return_val_if_fail (region_name != NULL, FALSE);
2194
2195   if (flags_return)
2196     *flags_return = 0;
2197
2198   region_quark = g_quark_try_string (region_name);
2199
2200   if (!region_quark)
2201     return FALSE;
2202
2203   priv = context->priv;
2204
2205   g_assert (priv->info_stack != NULL);
2206   info = priv->info_stack->data;
2207
2208   if (region_find (info->regions, region_quark, &position))
2209     {
2210       if (flags_return)
2211         {
2212           GtkRegion *region;
2213
2214           region = &g_array_index (info->regions, GtkRegion, position);
2215           *flags_return = region->flags;
2216         }
2217       return TRUE;
2218     }
2219
2220   return FALSE;
2221 }
2222
2223 static gint
2224 style_property_values_cmp (gconstpointer bsearch_node1,
2225                            gconstpointer bsearch_node2)
2226 {
2227   const PropertyValue *val1 = bsearch_node1;
2228   const PropertyValue *val2 = bsearch_node2;
2229
2230   if (val1->widget_type != val2->widget_type)
2231     return val1->widget_type < val2->widget_type ? -1 : 1;
2232
2233   if (val1->pspec != val2->pspec)
2234     return val1->pspec < val2->pspec ? -1 : 1;
2235
2236   if (val1->state != val2->state)
2237     return val1->state < val2->state ? -1 : 1;
2238
2239   return 0;
2240 }
2241
2242 GtkCssValue *
2243 _gtk_style_context_peek_property (GtkStyleContext *context,
2244                                   const char      *property_name)
2245 {
2246   StyleData *data = style_data_lookup (context);
2247
2248   return _gtk_css_computed_values_get_value_by_name (data->store, property_name);
2249 }
2250
2251 double
2252 _gtk_style_context_get_number (GtkStyleContext *context,
2253                                const char      *property_name,
2254                                double           one_hundred_percent)
2255 {
2256   GtkCssValue *value;
2257   
2258   value = _gtk_style_context_peek_property (context, property_name);
2259   return _gtk_css_number_value_get (value, one_hundred_percent);
2260 }
2261
2262 const GValue *
2263 _gtk_style_context_peek_style_property (GtkStyleContext *context,
2264                                         GType            widget_type,
2265                                         GtkStateFlags    state,
2266                                         GParamSpec      *pspec)
2267 {
2268   GtkStyleContextPrivate *priv;
2269   PropertyValue *pcache, key = { 0 };
2270   StyleData *data;
2271   guint i;
2272
2273   priv = context->priv;
2274
2275   gtk_style_context_save (context);
2276   gtk_style_context_set_state (context, state);
2277   data = style_data_lookup (context);
2278   gtk_style_context_restore (context);
2279
2280   key.widget_type = widget_type;
2281   key.state = state;
2282   key.pspec = pspec;
2283
2284   /* need value cache array */
2285   if (!data->property_cache)
2286     data->property_cache = g_array_new (FALSE, FALSE, sizeof (PropertyValue));
2287   else
2288     {
2289       pcache = bsearch (&key,
2290                         data->property_cache->data, data->property_cache->len,
2291                         sizeof (PropertyValue), style_property_values_cmp);
2292       if (pcache)
2293         return &pcache->value;
2294     }
2295
2296   i = 0;
2297   while (i < data->property_cache->len &&
2298          style_property_values_cmp (&key, &g_array_index (data->property_cache, PropertyValue, i)) >= 0)
2299     i++;
2300
2301   g_array_insert_val (data->property_cache, i, key);
2302   pcache = &g_array_index (data->property_cache, PropertyValue, i);
2303
2304   /* cache miss, initialize value type, then set contents */
2305   g_param_spec_ref (pcache->pspec);
2306   g_value_init (&pcache->value, G_PARAM_SPEC_VALUE_TYPE (pspec));
2307
2308   if (priv->widget || priv->widget_path)
2309     {
2310       if (gtk_style_provider_get_style_property (GTK_STYLE_PROVIDER (priv->cascade),
2311                                                  priv->widget ? gtk_widget_get_path (priv->widget)
2312                                                               : priv->widget_path,
2313                                                  state, pspec, &pcache->value))
2314         {
2315           /* Resolve symbolic colors to GdkColor/GdkRGBA */
2316           if (G_VALUE_TYPE (&pcache->value) == GTK_TYPE_SYMBOLIC_COLOR)
2317             {
2318               GtkSymbolicColor *color;
2319               GdkRGBA rgba;
2320
2321               color = g_value_dup_boxed (&pcache->value);
2322
2323               g_value_unset (&pcache->value);
2324
2325               if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
2326                 g_value_init (&pcache->value, GDK_TYPE_RGBA);
2327               else
2328                 g_value_init (&pcache->value, GDK_TYPE_COLOR);
2329
2330               if (_gtk_style_context_resolve_color (context, color, &rgba))
2331                 {
2332                   if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
2333                     g_value_set_boxed (&pcache->value, &rgba);
2334                   else
2335                     {
2336                       GdkColor rgb;
2337
2338                       rgb.red = rgba.red * 65535. + 0.5;
2339                       rgb.green = rgba.green * 65535. + 0.5;
2340                       rgb.blue = rgba.blue * 65535. + 0.5;
2341
2342                       g_value_set_boxed (&pcache->value, &rgb);
2343                     }
2344                 }
2345               else
2346                 g_param_value_set_default (pspec, &pcache->value);
2347
2348               gtk_symbolic_color_unref (color);
2349             }
2350
2351           return &pcache->value;
2352         }
2353     }
2354
2355   /* not supplied by any provider, revert to default */
2356   g_param_value_set_default (pspec, &pcache->value);
2357
2358   return &pcache->value;
2359 }
2360
2361 /**
2362  * gtk_style_context_get_style_property:
2363  * @context: a #GtkStyleContext
2364  * @property_name: the name of the widget style property
2365  * @value: Return location for the property value
2366  *
2367  * Gets the value for a widget style property.
2368  *
2369  * When @value is no longer needed, g_value_unset() must be called
2370  * to free any allocated memory.
2371  **/
2372 void
2373 gtk_style_context_get_style_property (GtkStyleContext *context,
2374                                       const gchar     *property_name,
2375                                       GValue          *value)
2376 {
2377   GtkStyleContextPrivate *priv;
2378   GtkWidgetClass *widget_class;
2379   GtkStateFlags state;
2380   GParamSpec *pspec;
2381   const GValue *peek_value;
2382   GType widget_type;
2383
2384   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2385   g_return_if_fail (property_name != NULL);
2386   g_return_if_fail (value != NULL);
2387
2388   priv = context->priv;
2389
2390   if (priv->widget)
2391     {
2392       widget_type = G_OBJECT_TYPE (priv->widget);
2393     }
2394   else
2395     {
2396       if (!priv->widget_path)
2397         return;
2398
2399       widget_type = gtk_widget_path_get_object_type (priv->widget_path);
2400
2401       if (!g_type_is_a (widget_type, GTK_TYPE_WIDGET))
2402         {
2403           g_warning ("%s: can't get style properties for non-widget class `%s'",
2404                      G_STRLOC,
2405                      g_type_name (widget_type));
2406           return;
2407         }
2408     }
2409
2410   widget_class = g_type_class_ref (widget_type);
2411   pspec = gtk_widget_class_find_style_property (widget_class, property_name);
2412   g_type_class_unref (widget_class);
2413
2414   if (!pspec)
2415     {
2416       g_warning ("%s: widget class `%s' has no style property named `%s'",
2417                  G_STRLOC,
2418                  g_type_name (widget_type),
2419                  property_name);
2420       return;
2421     }
2422
2423   state = gtk_style_context_get_state (context);
2424   peek_value = _gtk_style_context_peek_style_property (context, widget_type,
2425                                                        state, pspec);
2426
2427   if (G_VALUE_TYPE (value) == G_VALUE_TYPE (peek_value))
2428     g_value_copy (peek_value, value);
2429   else if (g_value_type_transformable (G_VALUE_TYPE (peek_value), G_VALUE_TYPE (value)))
2430     g_value_transform (peek_value, value);
2431   else
2432     g_warning ("can't retrieve style property `%s' of type `%s' as value of type `%s'",
2433                pspec->name,
2434                G_VALUE_TYPE_NAME (peek_value),
2435                G_VALUE_TYPE_NAME (value));
2436 }
2437
2438 /**
2439  * gtk_style_context_get_style_valist:
2440  * @context: a #GtkStyleContext
2441  * @args: va_list of property name/return location pairs, followed by %NULL
2442  *
2443  * Retrieves several widget style properties from @context according to the
2444  * current style.
2445  *
2446  * Since: 3.0
2447  **/
2448 void
2449 gtk_style_context_get_style_valist (GtkStyleContext *context,
2450                                     va_list          args)
2451 {
2452   GtkStyleContextPrivate *priv;
2453   const gchar *prop_name;
2454   GtkStateFlags state;
2455   GType widget_type;
2456
2457   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2458
2459   prop_name = va_arg (args, const gchar *);
2460   priv = context->priv;
2461
2462   if (priv->widget)
2463     {
2464       widget_type = G_OBJECT_TYPE (priv->widget);
2465     }
2466   else
2467     {
2468       if (!priv->widget_path)
2469         return;
2470
2471       widget_type = gtk_widget_path_get_object_type (priv->widget_path);
2472
2473       if (!g_type_is_a (widget_type, GTK_TYPE_WIDGET))
2474         {
2475           g_warning ("%s: can't get style properties for non-widget class `%s'",
2476                      G_STRLOC,
2477                      g_type_name (widget_type));
2478           return;
2479         }
2480     }
2481
2482   state = gtk_style_context_get_state (context);
2483
2484   while (prop_name)
2485     {
2486       GtkWidgetClass *widget_class;
2487       GParamSpec *pspec;
2488       const GValue *peek_value;
2489       gchar *error;
2490
2491       widget_class = g_type_class_ref (widget_type);
2492       pspec = gtk_widget_class_find_style_property (widget_class, prop_name);
2493       g_type_class_unref (widget_class);
2494
2495       if (!pspec)
2496         {
2497           g_warning ("%s: widget class `%s' has no style property named `%s'",
2498                      G_STRLOC,
2499                      g_type_name (widget_type),
2500                      prop_name);
2501           break;
2502         }
2503
2504       peek_value = _gtk_style_context_peek_style_property (context, widget_type,
2505                                                            state, pspec);
2506
2507       G_VALUE_LCOPY (peek_value, args, 0, &error);
2508
2509       if (error)
2510         {
2511           g_warning ("can't retrieve style property `%s' of type `%s': %s",
2512                      pspec->name,
2513                      G_VALUE_TYPE_NAME (peek_value),
2514                      error);
2515           g_free (error);
2516           break;
2517         }
2518
2519       prop_name = va_arg (args, const gchar *);
2520     }
2521 }
2522
2523 /**
2524  * gtk_style_context_get_style:
2525  * @context: a #GtkStyleContext
2526  * @...: property name /return value pairs, followed by %NULL
2527  *
2528  * Retrieves several widget style properties from @context according to the
2529  * current style.
2530  *
2531  * Since: 3.0
2532  **/
2533 void
2534 gtk_style_context_get_style (GtkStyleContext *context,
2535                              ...)
2536 {
2537   va_list args;
2538
2539   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2540
2541   va_start (args, context);
2542   gtk_style_context_get_style_valist (context, args);
2543   va_end (args);
2544 }
2545
2546
2547 /**
2548  * gtk_style_context_lookup_icon_set:
2549  * @context: a #GtkStyleContext
2550  * @stock_id: an icon name
2551  *
2552  * Looks up @stock_id in the icon factories associated to @context and
2553  * the default icon factory, returning an icon set if found, otherwise
2554  * %NULL.
2555  *
2556  * Returns: (transfer none): The looked  up %GtkIconSet, or %NULL
2557  **/
2558 GtkIconSet *
2559 gtk_style_context_lookup_icon_set (GtkStyleContext *context,
2560                                    const gchar     *stock_id)
2561 {
2562   GtkStyleContextPrivate *priv;
2563
2564   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
2565   g_return_val_if_fail (stock_id != NULL, NULL);
2566
2567   priv = context->priv;
2568   g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
2569
2570   return gtk_icon_factory_lookup_default (stock_id);
2571 }
2572
2573 /**
2574  * gtk_style_context_set_screen:
2575  * @context: a #GtkStyleContext
2576  * @screen: a #GdkScreen
2577  *
2578  * Attaches @context to the given screen.
2579  *
2580  * The screen is used to add style information from 'global' style
2581  * providers, such as the screens #GtkSettings instance.
2582  *
2583  * If you are using a #GtkStyleContext returned from
2584  * gtk_widget_get_style_context(), you do not need to
2585  * call this yourself.
2586  *
2587  * Since: 3.0
2588  **/
2589 void
2590 gtk_style_context_set_screen (GtkStyleContext *context,
2591                               GdkScreen       *screen)
2592 {
2593   GtkStyleContextPrivate *priv;
2594
2595   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2596   g_return_if_fail (GDK_IS_SCREEN (screen));
2597
2598   priv = context->priv;
2599   if (priv->screen == screen)
2600     return;
2601
2602   if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
2603     {
2604       g_object_unref (priv->cascade);
2605       priv->cascade = _gtk_style_cascade_get_for_screen (screen);
2606       g_object_ref (priv->cascade);
2607     }
2608   else
2609     {
2610       _gtk_style_cascade_set_parent (priv->cascade, _gtk_style_cascade_get_for_screen (screen));
2611     }
2612
2613   priv->screen = screen;
2614
2615   g_object_notify (G_OBJECT (context), "screen");
2616
2617   gtk_style_context_invalidate (context);
2618 }
2619
2620 /**
2621  * gtk_style_context_get_screen:
2622  * @context: a #GtkStyleContext
2623  *
2624  * Returns the #GdkScreen to which @context is attached.
2625  *
2626  * Returns: (transfer none): a #GdkScreen.
2627  **/
2628 GdkScreen *
2629 gtk_style_context_get_screen (GtkStyleContext *context)
2630 {
2631   GtkStyleContextPrivate *priv;
2632
2633   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
2634
2635   priv = context->priv;
2636   return priv->screen;
2637 }
2638
2639 /**
2640  * gtk_style_context_set_direction:
2641  * @context: a #GtkStyleContext
2642  * @direction: the new direction.
2643  *
2644  * Sets the reading direction for rendering purposes.
2645  *
2646  * If you are using a #GtkStyleContext returned from
2647  * gtk_widget_get_style_context(), you do not need to
2648  * call this yourself.
2649  *
2650  * Since: 3.0
2651  **/
2652 void
2653 gtk_style_context_set_direction (GtkStyleContext  *context,
2654                                  GtkTextDirection  direction)
2655 {
2656   GtkStyleContextPrivate *priv;
2657
2658   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2659
2660   priv = context->priv;
2661   priv->direction = direction;
2662
2663   g_object_notify (G_OBJECT (context), "direction");
2664 }
2665
2666 /**
2667  * gtk_style_context_get_direction:
2668  * @context: a #GtkStyleContext
2669  *
2670  * Returns the widget direction used for rendering.
2671  *
2672  * Returns: the widget direction
2673  *
2674  * Since: 3.0
2675  **/
2676 GtkTextDirection
2677 gtk_style_context_get_direction (GtkStyleContext *context)
2678 {
2679   GtkStyleContextPrivate *priv;
2680
2681   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), GTK_TEXT_DIR_LTR);
2682
2683   priv = context->priv;
2684   return priv->direction;
2685 }
2686
2687 /**
2688  * gtk_style_context_set_junction_sides:
2689  * @context: a #GtkStyleContext
2690  * @sides: sides where rendered elements are visually connected to
2691  *     other elements
2692  *
2693  * Sets the sides where rendered elements (mostly through
2694  * gtk_render_frame()) will visually connect with other visual elements.
2695  *
2696  * This is merely a hint that may or may not be honored
2697  * by theming engines.
2698  *
2699  * Container widgets are expected to set junction hints as appropriate
2700  * for their children, so it should not normally be necessary to call
2701  * this function manually.
2702  *
2703  * Since: 3.0
2704  **/
2705 void
2706 gtk_style_context_set_junction_sides (GtkStyleContext  *context,
2707                                       GtkJunctionSides  sides)
2708 {
2709   GtkStyleContextPrivate *priv;
2710   GtkStyleInfo *info;
2711
2712   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2713
2714   priv = context->priv;
2715   info = priv->info_stack->data;
2716   info->junction_sides = sides;
2717 }
2718
2719 /**
2720  * gtk_style_context_get_junction_sides:
2721  * @context: a #GtkStyleContext
2722  *
2723  * Returns the sides where rendered elements connect visually with others.
2724  *
2725  * Returns: the junction sides
2726  *
2727  * Since: 3.0
2728  **/
2729 GtkJunctionSides
2730 gtk_style_context_get_junction_sides (GtkStyleContext *context)
2731 {
2732   GtkStyleContextPrivate *priv;
2733   GtkStyleInfo *info;
2734
2735   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
2736
2737   priv = context->priv;
2738   info = priv->info_stack->data;
2739   return info->junction_sides;
2740 }
2741
2742 static GtkSymbolicColor *
2743 gtk_style_context_color_lookup_func (gpointer    contextp,
2744                                      const char *name)
2745 {
2746   GtkStyleContext *context = contextp;
2747
2748   return _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade), name);
2749 }
2750
2751 GtkCssValue *
2752 _gtk_style_context_resolve_color_value (GtkStyleContext  *context,
2753                                         GtkSymbolicColor *color)
2754 {
2755   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
2756   g_return_val_if_fail (color != NULL, FALSE);
2757
2758   return _gtk_symbolic_color_resolve_full (color,
2759                                            gtk_style_context_color_lookup_func,
2760                                            context);
2761 }
2762
2763
2764 gboolean
2765 _gtk_style_context_resolve_color (GtkStyleContext  *context,
2766                                   GtkSymbolicColor *color,
2767                                   GdkRGBA          *result)
2768 {
2769   GtkCssValue *val;
2770
2771   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
2772   g_return_val_if_fail (color != NULL, FALSE);
2773   g_return_val_if_fail (result != NULL, FALSE);
2774
2775   val = _gtk_symbolic_color_resolve_full (color,
2776                                           gtk_style_context_color_lookup_func,
2777                                           context);
2778   if (val == NULL)
2779     return FALSE;
2780
2781   *result = *_gtk_css_rgba_value_get_rgba (val);
2782   _gtk_css_value_unref (val);
2783   return TRUE;
2784 }
2785
2786 /**
2787  * gtk_style_context_lookup_color:
2788  * @context: a #GtkStyleContext
2789  * @color_name: color name to lookup
2790  * @color: (out): Return location for the looked up color
2791  *
2792  * Looks up and resolves a color name in the @context color map.
2793  *
2794  * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise
2795  **/
2796 gboolean
2797 gtk_style_context_lookup_color (GtkStyleContext *context,
2798                                 const gchar     *color_name,
2799                                 GdkRGBA         *color)
2800 {
2801   GtkSymbolicColor *sym_color;
2802
2803   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
2804   g_return_val_if_fail (color_name != NULL, FALSE);
2805   g_return_val_if_fail (color != NULL, FALSE);
2806
2807   sym_color = gtk_style_context_color_lookup_func (context, color_name);
2808   if (sym_color == NULL)
2809     return FALSE;
2810
2811   return _gtk_style_context_resolve_color (context, sym_color, color);
2812 }
2813
2814 /**
2815  * gtk_style_context_notify_state_change:
2816  * @context: a #GtkStyleContext
2817  * @window: a #GdkWindow
2818  * @region_id: (allow-none): animatable region to notify on, or %NULL.
2819  *     See gtk_style_context_push_animatable_region()
2820  * @state: state to trigger transition for
2821  * @state_value: %TRUE if @state is the state we are changing to,
2822  *     %FALSE if we are changing away from it
2823  *
2824  * Notifies a state change on @context, so if the current style makes use
2825  * of transition animations, one will be started so all rendered elements
2826  * under @region_id are animated for state @state being set to value
2827  * @state_value.
2828  *
2829  * The @window parameter is used in order to invalidate the rendered area
2830  * as the animation runs, so make sure it is the same window that is being
2831  * rendered on by the gtk_render_*() functions.
2832  *
2833  * If @region_id is %NULL, all rendered elements using @context will be
2834  * affected by this state transition.
2835  *
2836  * As a practical example, a #GtkButton notifying a state transition on
2837  * the prelight state:
2838  * <programlisting>
2839  * gtk_style_context_notify_state_change (context,
2840  *                                        gtk_widget_get_window (widget),
2841  *                                        NULL,
2842  *                                        GTK_STATE_PRELIGHT,
2843  *                                        button->in_button);
2844  * </programlisting>
2845  *
2846  * Can be handled in the CSS file like this:
2847  * <programlisting>
2848  * GtkButton {
2849  *     background-color: &num;f00
2850  * }
2851  *
2852  * GtkButton:hover {
2853  *     background-color: &num;fff;
2854  *     transition: 200ms linear
2855  * }
2856  * </programlisting>
2857  *
2858  * This combination will animate the button background from red to white
2859  * if a pointer enters the button, and back to red if the pointer leaves
2860  * the button.
2861  *
2862  * Note that @state is used when finding the transition parameters, which
2863  * is why the style places the transition under the :hover pseudo-class.
2864  *
2865  * Since: 3.0
2866  **/
2867 void
2868 gtk_style_context_notify_state_change (GtkStyleContext *context,
2869                                        GdkWindow       *window,
2870                                        gpointer         region_id,
2871                                        GtkStateType     state,
2872                                        gboolean         state_value)
2873 {
2874   GtkStyleContextPrivate *priv;
2875   GtkAnimationDescription *desc;
2876   AnimationInfo *info;
2877   GtkStateFlags flags;
2878   GtkCssValue *v;
2879   StyleData *data;
2880
2881   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2882   g_return_if_fail (GDK_IS_WINDOW (window));
2883   g_return_if_fail (state > GTK_STATE_NORMAL && state <= GTK_STATE_FOCUSED);
2884
2885   priv = context->priv;
2886   g_return_if_fail (priv->widget != NULL || priv->widget_path != NULL);
2887
2888   state_value = (state_value == TRUE);
2889
2890   switch (state)
2891     {
2892     case GTK_STATE_ACTIVE:
2893       flags = GTK_STATE_FLAG_ACTIVE;
2894       break;
2895     case GTK_STATE_PRELIGHT:
2896       flags = GTK_STATE_FLAG_PRELIGHT;
2897       break;
2898     case GTK_STATE_SELECTED:
2899       flags = GTK_STATE_FLAG_SELECTED;
2900       break;
2901     case GTK_STATE_INSENSITIVE:
2902       flags = GTK_STATE_FLAG_INSENSITIVE;
2903       break;
2904     case GTK_STATE_INCONSISTENT:
2905       flags = GTK_STATE_FLAG_INCONSISTENT;
2906       break;
2907     case GTK_STATE_FOCUSED:
2908       flags = GTK_STATE_FLAG_FOCUSED;
2909       break;
2910     case GTK_STATE_NORMAL:
2911     default:
2912       flags = 0;
2913       break;
2914     }
2915
2916   /* Find out if there is any animation description for the given
2917    * state, it will fallback to the normal state as well if necessary.
2918    */
2919   gtk_style_context_save (context);
2920   gtk_style_context_set_state (context, flags);
2921   data = style_data_lookup (context);
2922   gtk_style_context_restore (context);
2923   v = _gtk_css_computed_values_get_value_by_name (data->store, "transition");
2924   if (!v)
2925     return;
2926   desc = _gtk_css_value_get_boxed (v);
2927   if (!desc)
2928     return;
2929
2930   if (_gtk_animation_description_get_duration (desc) == 0)
2931     return;
2932
2933   info = animation_info_lookup (context, region_id, state);
2934
2935   if (info &&
2936       info->target_value != state_value)
2937     {
2938       /* Target values are the opposite */
2939       if (!_gtk_timeline_get_loop (info->timeline))
2940         {
2941           /* Reverse the animation */
2942           if (_gtk_timeline_get_direction (info->timeline) == GTK_TIMELINE_DIRECTION_FORWARD)
2943             _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
2944           else
2945             _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_FORWARD);
2946
2947           info->target_value = state_value;
2948         }
2949       else
2950         {
2951           /* Take it out of its looping state */
2952           _gtk_timeline_set_loop (info->timeline, FALSE);
2953         }
2954     }
2955   else if (!info &&
2956            (!_gtk_animation_description_get_loop (desc) ||
2957             state_value))
2958     {
2959       info = animation_info_new (context, region_id,
2960                                  _gtk_animation_description_get_duration (desc),
2961                                  _gtk_animation_description_get_progress_type (desc),
2962                                  _gtk_animation_description_get_loop (desc),
2963                                  state, state_value, window);
2964
2965       priv->animations = g_slist_prepend (priv->animations, info);
2966       priv->animations_invalidated = TRUE;
2967     }
2968 }
2969
2970 /**
2971  * gtk_style_context_cancel_animations:
2972  * @context: a #GtkStyleContext
2973  * @region_id: (allow-none): animatable region to stop, or %NULL.
2974  *     See gtk_style_context_push_animatable_region()
2975  *
2976  * Stops all running animations for @region_id and all animatable
2977  * regions underneath.
2978  *
2979  * A %NULL @region_id will stop all ongoing animations in @context,
2980  * when dealing with a #GtkStyleContext obtained through
2981  * gtk_widget_get_style_context(), this is normally done for you
2982  * in all circumstances you would expect all widget to be stopped,
2983  * so this should be only used in complex widgets with different
2984  * animatable regions.
2985  *
2986  * Since: 3.0
2987  **/
2988 void
2989 gtk_style_context_cancel_animations (GtkStyleContext *context,
2990                                      gpointer         region_id)
2991 {
2992   GtkStyleContextPrivate *priv;
2993   AnimationInfo *info;
2994   GSList *l;
2995
2996   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
2997
2998   priv = context->priv;
2999   l = priv->animations;
3000
3001   while (l)
3002     {
3003       info = l->data;
3004       l = l->next;
3005
3006       if (!region_id ||
3007           info->region_id == region_id ||
3008           g_slist_find (info->parent_regions, region_id))
3009         {
3010           priv->animations = g_slist_remove (priv->animations, info);
3011           animation_info_free (info);
3012         }
3013     }
3014 }
3015
3016 static gboolean
3017 is_parent_of (GdkWindow *parent,
3018               GdkWindow *child)
3019 {
3020   GtkWidget *child_widget, *parent_widget;
3021   GdkWindow *window;
3022
3023   gdk_window_get_user_data (child, (gpointer *) &child_widget);
3024   gdk_window_get_user_data (parent, (gpointer *) &parent_widget);
3025
3026   if (child_widget != parent_widget &&
3027       !gtk_widget_is_ancestor (child_widget, parent_widget))
3028     return FALSE;
3029
3030   window = child;
3031
3032   while (window)
3033     {
3034       if (window == parent)
3035         return TRUE;
3036
3037       window = gdk_window_get_parent (window);
3038     }
3039
3040   return FALSE;
3041 }
3042
3043 /**
3044  * gtk_style_context_scroll_animations:
3045  * @context: a #GtkStyleContext
3046  * @window: a #GdkWindow used previously in
3047  *          gtk_style_context_notify_state_change()
3048  * @dx: Amount to scroll in the X axis
3049  * @dy: Amount to scroll in the Y axis
3050  *
3051  * This function is analogous to gdk_window_scroll(), and
3052  * should be called together with it so the invalidation
3053  * areas for any ongoing animation are scrolled together
3054  * with it.
3055  *
3056  * Since: 3.0
3057  **/
3058 void
3059 gtk_style_context_scroll_animations (GtkStyleContext *context,
3060                                      GdkWindow       *window,
3061                                      gint             dx,
3062                                      gint             dy)
3063 {
3064   GtkStyleContextPrivate *priv;
3065   AnimationInfo *info;
3066   GSList *l;
3067
3068   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3069   g_return_if_fail (GDK_IS_WINDOW (window));
3070
3071   priv = context->priv;
3072   l = priv->animations;
3073
3074   while (l)
3075     {
3076       info = l->data;
3077       l = l->next;
3078
3079       if (info->invalidation_region &&
3080           (window == info->window ||
3081            is_parent_of (window, info->window)))
3082         cairo_region_translate (info->invalidation_region, dx, dy);
3083     }
3084 }
3085
3086 /**
3087  * gtk_style_context_push_animatable_region:
3088  * @context: a #GtkStyleContext
3089  * @region_id: unique identifier for the animatable region
3090  *
3091  * Pushes an animatable region, so all further gtk_render_*() calls between
3092  * this call and the following gtk_style_context_pop_animatable_region()
3093  * will potentially show transition animations for this region if
3094  * gtk_style_context_notify_state_change() is called for a given state,
3095  * and the current theme/style defines transition animations for state
3096  * changes.
3097  *
3098  * The @region_id used must be unique in @context so the theming engine
3099  * can uniquely identify rendered elements subject to a state transition.
3100  *
3101  * Since: 3.0
3102  **/
3103 void
3104 gtk_style_context_push_animatable_region (GtkStyleContext *context,
3105                                           gpointer         region_id)
3106 {
3107   GtkStyleContextPrivate *priv;
3108
3109   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3110   g_return_if_fail (region_id != NULL);
3111
3112   priv = context->priv;
3113   priv->animation_regions = g_slist_prepend (priv->animation_regions, region_id);
3114 }
3115
3116 /**
3117  * gtk_style_context_pop_animatable_region:
3118  * @context: a #GtkStyleContext
3119  *
3120  * Pops an animatable region from @context.
3121  * See gtk_style_context_push_animatable_region().
3122  *
3123  * Since: 3.0
3124  **/
3125 void
3126 gtk_style_context_pop_animatable_region (GtkStyleContext *context)
3127 {
3128   GtkStyleContextPrivate *priv;
3129
3130   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3131
3132   priv = context->priv;
3133   priv->animation_regions = g_slist_delete_link (priv->animation_regions,
3134                                                  priv->animation_regions);
3135 }
3136
3137 void
3138 _gtk_style_context_invalidate_animation_areas (GtkStyleContext *context)
3139 {
3140   GtkStyleContextPrivate *priv;
3141   GSList *l;
3142
3143   priv = context->priv;
3144
3145   for (l = priv->animations; l; l = l->next)
3146     {
3147       AnimationInfo *info;
3148
3149       info = l->data;
3150
3151       /* A NULL invalidation region means it has to be recreated on
3152        * the next expose event, this happens usually after a widget
3153        * allocation change, so the next expose after it will update
3154        * the invalidation region.
3155        */
3156       if (info->invalidation_region)
3157         {
3158           cairo_region_destroy (info->invalidation_region);
3159           info->invalidation_region = NULL;
3160         }
3161     }
3162
3163   priv->animations_invalidated = TRUE;
3164 }
3165
3166 void
3167 _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
3168                                              GtkWidget       *widget)
3169 {
3170   GtkStyleContextPrivate *priv;
3171   GSList *l;
3172
3173   priv = context->priv;
3174
3175   if (!priv->animations_invalidated)
3176     return;
3177
3178   l = priv->animations;
3179
3180   while (l)
3181     {
3182       AnimationInfo *info;
3183       gint rel_x, rel_y;
3184       GSList *cur;
3185       guint i;
3186
3187       cur = l;
3188       info = cur->data;
3189       l = l->next;
3190
3191       if (info->invalidation_region)
3192         continue;
3193
3194       if (info->rectangles->len == 0)
3195         continue;
3196
3197       info->invalidation_region = cairo_region_create ();
3198       _gtk_widget_get_translation_to_window (widget, info->window, &rel_x, &rel_y);
3199
3200       for (i = 0; i < info->rectangles->len; i++)
3201         {
3202           cairo_rectangle_int_t *rect;
3203
3204           rect = &g_array_index (info->rectangles, cairo_rectangle_int_t, i);
3205
3206           /* These are widget relative coordinates,
3207            * so have them inverted to be window relative
3208            */
3209           rect->x -= rel_x;
3210           rect->y -= rel_y;
3211
3212           cairo_region_union_rectangle (info->invalidation_region, rect);
3213         }
3214
3215       g_array_remove_range (info->rectangles, 0, info->rectangles->len);
3216     }
3217
3218   priv->animations_invalidated = FALSE;
3219 }
3220
3221 static void
3222 store_animation_region (GtkStyleContext *context,
3223                         gdouble          x,
3224                         gdouble          y,
3225                         gdouble          width,
3226                         gdouble          height)
3227 {
3228   GtkStyleContextPrivate *priv;
3229   GSList *l;
3230
3231   priv = context->priv;
3232
3233   if (!priv->animations_invalidated)
3234     return;
3235
3236   for (l = priv->animations; l; l = l->next)
3237     {
3238       AnimationInfo *info;
3239
3240       info = l->data;
3241
3242       /* The animation doesn't need updating
3243        * the invalidation area, bail out.
3244        */
3245       if (info->invalidation_region)
3246         continue;
3247
3248       if (context_has_animatable_region (context, info->region_id))
3249         {
3250           cairo_rectangle_int_t rect;
3251
3252           rect.x = (gint) x;
3253           rect.y = (gint) y;
3254           rect.width = (gint) width;
3255           rect.height = (gint) height;
3256
3257           g_array_append_val (info->rectangles, rect);
3258
3259           if (!info->parent_regions)
3260             {
3261               GSList *parent_regions;
3262
3263               parent_regions = g_slist_find (priv->animation_regions, info->region_id);
3264               info->parent_regions = g_slist_copy (parent_regions);
3265             }
3266         }
3267     }
3268 }
3269
3270 static void
3271 gtk_style_context_do_invalidate (GtkStyleContext *context,
3272                                  gboolean         clear_caches)
3273 {
3274   GtkStyleContextPrivate *priv;
3275
3276   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3277
3278   priv = context->priv;
3279
3280   /* Avoid reentrancy */
3281   if (priv->invalidating_context)
3282     return;
3283
3284   priv->invalidating_context = TRUE;
3285
3286   if (clear_caches)
3287     {
3288       GSList *list;
3289
3290       for (list = priv->info_stack; list; list = list->next)
3291         {
3292           GtkStyleInfo *info = list->data;
3293           info->data = NULL;
3294         }
3295       g_hash_table_remove_all (priv->style_data);
3296     }
3297
3298   g_signal_emit (context, signals[CHANGED], 0);
3299
3300   priv->invalidating_context = FALSE;
3301 }
3302
3303 void
3304 _gtk_style_context_validate (GtkStyleContext *context,
3305                              GtkCssChange     change)
3306 {
3307   GtkStyleContextPrivate *priv;
3308   GSList *list;
3309
3310   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3311
3312   priv = context->priv;
3313
3314   change |= priv->pending_changes;
3315
3316   if (!priv->invalid && change == 0)
3317     return;
3318
3319   priv->pending_changes = 0;
3320   gtk_style_context_set_invalid (context, FALSE);
3321
3322   /* Try to avoid invalidating if we can */
3323   if (change & GTK_STYLE_CONTEXT_RADICAL_CHANGE)
3324     {
3325       priv->relevant_changes = GTK_CSS_CHANGE_ANY;
3326     }
3327   else
3328     {
3329       if (priv->relevant_changes == GTK_CSS_CHANGE_ANY)
3330         {
3331           GtkWidgetPath *path;
3332           GtkCssMatcher matcher;
3333
3334           path = create_query_path (context);
3335           _gtk_css_matcher_init (&matcher, path, ((GtkStyleInfo *) priv->info_stack->data)->state_flags);
3336
3337           priv->relevant_changes = _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
3338                                                                            &matcher);
3339           priv->relevant_changes &= ~GTK_STYLE_CONTEXT_RADICAL_CHANGE;
3340
3341           gtk_widget_path_unref (path);
3342         }
3343     }
3344
3345   if (priv->relevant_changes & change)
3346     {
3347       GtkStyleInfo *info = priv->info_stack->data;
3348       gboolean clear_cache = ((priv->relevant_changes & change) & ~GTK_STYLE_CONTEXT_CACHED_CHANGE) != 0;
3349
3350       info->data = NULL;
3351       gtk_style_context_do_invalidate (context, clear_cache);
3352     }
3353
3354   change = _gtk_css_change_for_child (change);
3355   for (list = priv->children; list; list = list->next)
3356     {
3357       _gtk_style_context_validate (list->data, change);
3358     }
3359 }
3360
3361 void
3362 _gtk_style_context_queue_invalidate (GtkStyleContext *context,
3363                                      GtkCssChange     change)
3364 {
3365   GtkStyleContextPrivate *priv;
3366
3367   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3368   g_return_if_fail (change != 0);
3369
3370   priv = context->priv;
3371
3372   if (priv->widget == NULL && priv->widget_path == NULL)
3373     return;
3374
3375   priv->pending_changes |= change;
3376   gtk_style_context_set_invalid (context, TRUE);
3377 }
3378
3379 /**
3380  * gtk_style_context_invalidate:
3381  * @context: a #GtkStyleContext.
3382  *
3383  * Invalidates @context style information, so it will be reconstructed
3384  * again.
3385  *
3386  * If you're using a #GtkStyleContext returned from
3387  * gtk_widget_get_style_context(), you do not need to
3388  * call this yourself.
3389  *
3390  * Since: 3.0
3391  **/
3392 void
3393 gtk_style_context_invalidate (GtkStyleContext *context)
3394 {
3395   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3396
3397   gtk_style_context_do_invalidate (context, TRUE);
3398 }
3399
3400 /**
3401  * gtk_style_context_set_background:
3402  * @context: a #GtkStyleContext
3403  * @window: a #GdkWindow
3404  *
3405  * Sets the background of @window to the background pattern or
3406  * color specified in @context for its current state.
3407  *
3408  * Since: 3.0
3409  **/
3410 void
3411 gtk_style_context_set_background (GtkStyleContext *context,
3412                                   GdkWindow       *window)
3413 {
3414   GtkStateFlags state;
3415   cairo_pattern_t *pattern;
3416   GdkRGBA *color;
3417
3418   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3419   g_return_if_fail (GDK_IS_WINDOW (window));
3420
3421   state = gtk_style_context_get_state (context);
3422   gtk_style_context_get (context, state,
3423                          "background-image", &pattern,
3424                          NULL);
3425   if (pattern)
3426     {
3427       gdk_window_set_background_pattern (window, pattern);
3428       cairo_pattern_destroy (pattern);
3429       return;
3430     }
3431
3432   gtk_style_context_get (context, state,
3433                          "background-color", &color,
3434                          NULL);
3435   if (color)
3436     {
3437       gdk_window_set_background_rgba (window, color);
3438       gdk_rgba_free (color);
3439     }
3440 }
3441
3442 /**
3443  * gtk_style_context_get_color:
3444  * @context: a #GtkStyleContext
3445  * @state: state to retrieve the color for
3446  * @color: (out): return value for the foreground color
3447  *
3448  * Gets the foreground color for a given state.
3449  *
3450  * Since: 3.0
3451  **/
3452 void
3453 gtk_style_context_get_color (GtkStyleContext *context,
3454                              GtkStateFlags    state,
3455                              GdkRGBA         *color)
3456 {
3457   GdkRGBA *c;
3458
3459   g_return_if_fail (color != NULL);
3460   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3461
3462   gtk_style_context_get (context,
3463                          state,
3464                          "color", &c,
3465                          NULL);
3466
3467   *color = *c;
3468   gdk_rgba_free (c);
3469 }
3470
3471 /**
3472  * gtk_style_context_get_background_color:
3473  * @context: a #GtkStyleContext
3474  * @state: state to retrieve the color for
3475  * @color: (out): return value for the background color
3476  *
3477  * Gets the background color for a given state.
3478  *
3479  * Since: 3.0
3480  **/
3481 void
3482 gtk_style_context_get_background_color (GtkStyleContext *context,
3483                                         GtkStateFlags    state,
3484                                         GdkRGBA         *color)
3485 {
3486   GdkRGBA *c;
3487
3488   g_return_if_fail (color != NULL);
3489   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3490
3491   gtk_style_context_get (context,
3492                          state,
3493                          "background-color", &c,
3494                          NULL);
3495
3496   *color = *c;
3497   gdk_rgba_free (c);
3498 }
3499
3500 /**
3501  * gtk_style_context_get_border_color:
3502  * @context: a #GtkStyleContext
3503  * @state: state to retrieve the color for
3504  * @color: (out): return value for the border color
3505  *
3506  * Gets the border color for a given state.
3507  *
3508  * Since: 3.0
3509  **/
3510 void
3511 gtk_style_context_get_border_color (GtkStyleContext *context,
3512                                     GtkStateFlags    state,
3513                                     GdkRGBA         *color)
3514 {
3515   GdkRGBA *c;
3516
3517   g_return_if_fail (color != NULL);
3518   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3519
3520   gtk_style_context_get (context,
3521                          state,
3522                          "border-color", &c,
3523                          NULL);
3524
3525   *color = *c;
3526   gdk_rgba_free (c);
3527 }
3528
3529 /**
3530  * gtk_style_context_get_border:
3531  * @context: a #GtkStyleContext
3532  * @state: state to retrieve the border for
3533  * @border: (out): return value for the border settings
3534  *
3535  * Gets the border for a given state as a #GtkBorder.
3536  * See %GTK_STYLE_PROPERTY_BORDER_WIDTH.
3537  *
3538  * Since: 3.0
3539  **/
3540 void
3541 gtk_style_context_get_border (GtkStyleContext *context,
3542                               GtkStateFlags    state,
3543                               GtkBorder       *border)
3544 {
3545   int top, left, bottom, right;
3546
3547   g_return_if_fail (border != NULL);
3548   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3549
3550   gtk_style_context_get (context,
3551                          state,
3552                          "border-top-width", &top,
3553                          "border-left-width", &left,
3554                          "border-bottom-width", &bottom,
3555                          "border-right-width", &right,
3556                          NULL);
3557
3558   border->top = top;
3559   border->left = left;
3560   border->bottom = bottom;
3561   border->right = right;
3562 }
3563
3564 /**
3565  * gtk_style_context_get_padding:
3566  * @context: a #GtkStyleContext
3567  * @state: state to retrieve the padding for
3568  * @padding: (out): return value for the padding settings
3569  *
3570  * Gets the padding for a given state as a #GtkBorder.
3571  * See %GTK_STYLE_PROPERTY_PADDING.
3572  *
3573  * Since: 3.0
3574  **/
3575 void
3576 gtk_style_context_get_padding (GtkStyleContext *context,
3577                                GtkStateFlags    state,
3578                                GtkBorder       *padding)
3579 {
3580   int top, left, bottom, right;
3581
3582   g_return_if_fail (padding != NULL);
3583   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3584
3585   gtk_style_context_get (context,
3586                          state,
3587                          "padding-top", &top,
3588                          "padding-left", &left,
3589                          "padding-bottom", &bottom,
3590                          "padding-right", &right,
3591                          NULL);
3592
3593   padding->top = top;
3594   padding->left = left;
3595   padding->bottom = bottom;
3596   padding->right = right;
3597 }
3598
3599 /**
3600  * gtk_style_context_get_margin:
3601  * @context: a #GtkStyleContext
3602  * @state: state to retrieve the border for
3603  * @margin: (out): return value for the margin settings
3604  *
3605  * Gets the margin for a given state as a #GtkBorder.
3606  * See %GTK_STYLE_PROPERTY_MARGIN.
3607  *
3608  * Since: 3.0
3609  **/
3610 void
3611 gtk_style_context_get_margin (GtkStyleContext *context,
3612                               GtkStateFlags    state,
3613                               GtkBorder       *margin)
3614 {
3615   int top, left, bottom, right;
3616
3617   g_return_if_fail (margin != NULL);
3618   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3619
3620   gtk_style_context_get (context,
3621                          state,
3622                          "margin-top", &top,
3623                          "margin-left", &left,
3624                          "margin-bottom", &bottom,
3625                          "margin-right", &right,
3626                          NULL);
3627
3628   margin->top = top;
3629   margin->left = left;
3630   margin->bottom = bottom;
3631   margin->right = right;
3632 }
3633
3634 /**
3635  * gtk_style_context_get_font:
3636  * @context: a #GtkStyleContext
3637  * @state: state to retrieve the font for
3638  *
3639  * Returns the font description for a given state. The returned
3640  * object is const and will remain valid until the
3641  * #GtkStyleContext::changed signal happens.
3642  *
3643  * Returns: (transfer none): the #PangoFontDescription for the given
3644  *          state.  This object is owned by GTK+ and should not be
3645  *          freed.
3646  *
3647  * Since: 3.0
3648  **/
3649 const PangoFontDescription *
3650 gtk_style_context_get_font (GtkStyleContext *context,
3651                             GtkStateFlags    state)
3652 {
3653   GtkStyleContextPrivate *priv;
3654   StyleData *data;
3655   PangoFontDescription *description;
3656
3657   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
3658
3659   priv = context->priv;
3660   g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
3661
3662   gtk_style_context_save (context);
3663   gtk_style_context_set_state (context, state);
3664   data = style_data_lookup (context);
3665   gtk_style_context_restore (context);
3666
3667   /* Yuck, fonts are created on-demand but we don't return a ref.
3668    * Do bad things to achieve this requirement */
3669   description = g_object_get_data (G_OBJECT (data->store), "font-cache-for-get_font");
3670   if (description == NULL)
3671     {
3672       gtk_style_context_get (context, state, "font", &description, NULL);
3673       g_object_set_data_full (G_OBJECT (data->store),
3674                               "font-cache-for-get_font",
3675                               description,
3676                               (GDestroyNotify) pango_font_description_free);
3677     }
3678   return description;
3679 }
3680
3681 static void
3682 get_cursor_color (GtkStyleContext *context,
3683                   gboolean         primary,
3684                   GdkRGBA         *color)
3685 {
3686   GdkColor *style_color;
3687
3688   gtk_style_context_get_style (context,
3689                                primary ? "cursor-color" : "secondary-cursor-color",
3690                                &style_color,
3691                                NULL);
3692
3693   if (style_color)
3694     {
3695       color->red = style_color->red / 65535.0;
3696       color->green = style_color->green / 65535.0;
3697       color->blue = style_color->blue / 65535.0;
3698       color->alpha = 1;
3699
3700       gdk_color_free (style_color);
3701     }
3702   else
3703     {
3704       gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, color);
3705
3706       if (!primary)
3707       {
3708         GdkRGBA bg;
3709
3710         gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &bg);
3711
3712         color->red = (color->red + bg.red) * 0.5;
3713         color->green = (color->green + bg.green) * 0.5;
3714         color->blue = (color->blue + bg.blue) * 0.5;
3715       }
3716     }
3717 }
3718
3719 void
3720 _gtk_style_context_get_cursor_color (GtkStyleContext *context,
3721                                      GdkRGBA         *primary_color,
3722                                      GdkRGBA         *secondary_color)
3723 {
3724   if (primary_color)
3725     get_cursor_color (context, TRUE, primary_color);
3726
3727   if (secondary_color)
3728     get_cursor_color (context, FALSE, secondary_color);
3729 }
3730
3731 /* Paint methods */
3732
3733 /**
3734  * gtk_render_check:
3735  * @context: a #GtkStyleContext
3736  * @cr: a #cairo_t
3737  * @x: X origin of the rectangle
3738  * @y: Y origin of the rectangle
3739  * @width: rectangle width
3740  * @height: rectangle height
3741  *
3742  * Renders a checkmark (as in a #GtkCheckButton).
3743  *
3744  * The %GTK_STATE_FLAG_ACTIVE state determines whether the check is
3745  * on or off, and %GTK_STATE_FLAG_INCONSISTENT determines whether it
3746  * should be marked as undefined.
3747  *
3748  * <example>
3749  * <title>Typical checkmark rendering</title>
3750  * <inlinegraphic fileref="checks.png" format="PNG"/>
3751  * </example>
3752  *
3753  * Since: 3.0
3754  **/
3755 void
3756 gtk_render_check (GtkStyleContext *context,
3757                   cairo_t         *cr,
3758                   gdouble          x,
3759                   gdouble          y,
3760                   gdouble          width,
3761                   gdouble          height)
3762 {
3763   GtkStyleContextPrivate *priv;
3764   GtkThemingEngineClass *engine_class;
3765
3766   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3767   g_return_if_fail (cr != NULL);
3768
3769   if (width <= 0 || height <= 0)
3770     return;
3771
3772   priv = context->priv;
3773   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
3774
3775   cairo_save (cr);
3776
3777   store_animation_region (context, x, y, width, height);
3778
3779   _gtk_theming_engine_set_context (priv->theming_engine, context);
3780   engine_class->render_check (priv->theming_engine, cr,
3781                               x, y, width, height);
3782
3783   cairo_restore (cr);
3784 }
3785
3786 /**
3787  * gtk_render_option:
3788  * @context: a #GtkStyleContext
3789  * @cr: a #cairo_t
3790  * @x: X origin of the rectangle
3791  * @y: Y origin of the rectangle
3792  * @width: rectangle width
3793  * @height: rectangle height
3794  *
3795  * Renders an option mark (as in a #GtkRadioButton), the %GTK_STATE_FLAG_ACTIVE
3796  * state will determine whether the option is on or off, and
3797  * %GTK_STATE_FLAG_INCONSISTENT whether it should be marked as undefined.
3798  *
3799  * <example>
3800  * <title>Typical option mark rendering</title>
3801  * <inlinegraphic fileref="options.png" format="PNG"/>
3802  * </example>
3803  *
3804  * Since: 3.0
3805  **/
3806 void
3807 gtk_render_option (GtkStyleContext *context,
3808                    cairo_t         *cr,
3809                    gdouble          x,
3810                    gdouble          y,
3811                    gdouble          width,
3812                    gdouble          height)
3813 {
3814   GtkStyleContextPrivate *priv;
3815   GtkThemingEngineClass *engine_class;
3816
3817   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3818   g_return_if_fail (cr != NULL);
3819
3820   if (width <= 0 || height <= 0)
3821     return;
3822
3823   priv = context->priv;
3824   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
3825
3826   cairo_save (cr);
3827
3828   store_animation_region (context, x, y, width, height);
3829
3830   _gtk_theming_engine_set_context (priv->theming_engine, context);
3831   engine_class->render_option (priv->theming_engine, cr,
3832                                x, y, width, height);
3833
3834   cairo_restore (cr);
3835 }
3836
3837 /**
3838  * gtk_render_arrow:
3839  * @context: a #GtkStyleContext
3840  * @cr: a #cairo_t
3841  * @angle: arrow angle from 0 to 2 * %G_PI, being 0 the arrow pointing to the north
3842  * @x: X origin of the render area
3843  * @y: Y origin of the render area
3844  * @size: square side for render area
3845  *
3846  * Renders an arrow pointing to @angle.
3847  *
3848  * <example>
3849  * <title>Typical arrow rendering at 0, 1&solidus;2 &pi;, &pi; and 3&solidus;2 &pi;</title>
3850  * <inlinegraphic fileref="arrows.png" format="PNG"/>
3851  * </example>
3852  *
3853  * Since: 3.0
3854  **/
3855 void
3856 gtk_render_arrow (GtkStyleContext *context,
3857                   cairo_t         *cr,
3858                   gdouble          angle,
3859                   gdouble          x,
3860                   gdouble          y,
3861                   gdouble          size)
3862 {
3863   GtkStyleContextPrivate *priv;
3864   GtkThemingEngineClass *engine_class;
3865
3866   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3867   g_return_if_fail (cr != NULL);
3868
3869   if (size <= 0)
3870     return;
3871
3872   priv = context->priv;
3873   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
3874
3875   cairo_save (cr);
3876
3877   gtk_style_context_save (context);
3878   gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW);
3879
3880   store_animation_region (context, x, y, size, size);
3881
3882   _gtk_theming_engine_set_context (priv->theming_engine, context);
3883   engine_class->render_arrow (priv->theming_engine, cr,
3884                               angle, x, y, size);
3885
3886   gtk_style_context_restore (context);
3887   cairo_restore (cr);
3888 }
3889
3890 /**
3891  * gtk_render_background:
3892  * @context: a #GtkStyleContext
3893  * @cr: a #cairo_t
3894  * @x: X origin of the rectangle
3895  * @y: Y origin of the rectangle
3896  * @width: rectangle width
3897  * @height: rectangle height
3898  *
3899  * Renders the background of an element.
3900  *
3901  * <example>
3902  * <title>Typical background rendering, showing the effect of
3903  * <parameter>background-image</parameter>,
3904  * <parameter>border-width</parameter> and
3905  * <parameter>border-radius</parameter></title>
3906  * <inlinegraphic fileref="background.png" format="PNG"/>
3907  * </example>
3908  *
3909  * Since: 3.0.
3910  **/
3911 void
3912 gtk_render_background (GtkStyleContext *context,
3913                        cairo_t         *cr,
3914                        gdouble          x,
3915                        gdouble          y,
3916                        gdouble          width,
3917                        gdouble          height)
3918 {
3919   GtkStyleContextPrivate *priv;
3920   GtkThemingEngineClass *engine_class;
3921
3922   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3923   g_return_if_fail (cr != NULL);
3924
3925   if (width <= 0 || height <= 0)
3926     return;
3927
3928   priv = context->priv;
3929   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
3930
3931   cairo_save (cr);
3932
3933   store_animation_region (context, x, y, width, height);
3934
3935   _gtk_theming_engine_set_context (priv->theming_engine, context);
3936   engine_class->render_background (priv->theming_engine, cr, x, y, width, height);
3937
3938   cairo_restore (cr);
3939 }
3940
3941 /**
3942  * gtk_render_frame:
3943  * @context: a #GtkStyleContext
3944  * @cr: a #cairo_t
3945  * @x: X origin of the rectangle
3946  * @y: Y origin of the rectangle
3947  * @width: rectangle width
3948  * @height: rectangle height
3949  *
3950  * Renders a frame around the rectangle defined by @x, @y, @width, @height.
3951  *
3952  * <example>
3953  * <title>Examples of frame rendering, showing the effect of
3954  * <parameter>border-image</parameter>,
3955  * <parameter>border-color</parameter>,
3956  * <parameter>border-width</parameter>,
3957  * <parameter>border-radius</parameter> and
3958  * junctions</title>
3959  * <inlinegraphic fileref="frames.png" format="PNG"/>
3960  * </example>
3961  *
3962  * Since: 3.0
3963  **/
3964 void
3965 gtk_render_frame (GtkStyleContext *context,
3966                   cairo_t         *cr,
3967                   gdouble          x,
3968                   gdouble          y,
3969                   gdouble          width,
3970                   gdouble          height)
3971 {
3972   GtkStyleContextPrivate *priv;
3973   GtkThemingEngineClass *engine_class;
3974
3975   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
3976   g_return_if_fail (cr != NULL);
3977
3978   if (width <= 0 || height <= 0)
3979     return;
3980
3981   priv = context->priv;
3982   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
3983
3984   cairo_save (cr);
3985
3986   store_animation_region (context, x, y, width, height);
3987
3988   _gtk_theming_engine_set_context (priv->theming_engine, context);
3989   engine_class->render_frame (priv->theming_engine, cr, x, y, width, height);
3990
3991   cairo_restore (cr);
3992 }
3993
3994 /**
3995  * gtk_render_expander:
3996  * @context: a #GtkStyleContext
3997  * @cr: a #cairo_t
3998  * @x: X origin of the rectangle
3999  * @y: Y origin of the rectangle
4000  * @width: rectangle width
4001  * @height: rectangle height
4002  *
4003  * Renders an expander (as used in #GtkTreeView and #GtkExpander) in the area
4004  * defined by @x, @y, @width, @height. The state %GTK_STATE_FLAG_ACTIVE
4005  * determines whether the expander is collapsed or expanded.
4006  *
4007  * <example>
4008  * <title>Typical expander rendering</title>
4009  * <inlinegraphic fileref="expanders.png" format="PNG"/>
4010  * </example>
4011  *
4012  * Since: 3.0
4013  **/
4014 void
4015 gtk_render_expander (GtkStyleContext *context,
4016                      cairo_t         *cr,
4017                      gdouble          x,
4018                      gdouble          y,
4019                      gdouble          width,
4020                      gdouble          height)
4021 {
4022   GtkStyleContextPrivate *priv;
4023   GtkThemingEngineClass *engine_class;
4024
4025   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4026   g_return_if_fail (cr != NULL);
4027
4028   if (width <= 0 || height <= 0)
4029     return;
4030
4031   priv = context->priv;
4032   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4033
4034   cairo_save (cr);
4035
4036   store_animation_region (context, x, y, width, height);
4037
4038   _gtk_theming_engine_set_context (priv->theming_engine, context);
4039   engine_class->render_expander (priv->theming_engine, cr, x, y, width, height);
4040
4041   cairo_restore (cr);
4042 }
4043
4044 /**
4045  * gtk_render_focus:
4046  * @context: a #GtkStyleContext
4047  * @cr: a #cairo_t
4048  * @x: X origin of the rectangle
4049  * @y: Y origin of the rectangle
4050  * @width: rectangle width
4051  * @height: rectangle height
4052  *
4053  * Renders a focus indicator on the rectangle determined by @x, @y, @width, @height.
4054  * <example>
4055  * <title>Typical focus rendering</title>
4056  * <inlinegraphic fileref="focus.png" format="PNG"/>
4057  * </example>
4058  *
4059  * Since: 3.0
4060  **/
4061 void
4062 gtk_render_focus (GtkStyleContext *context,
4063                   cairo_t         *cr,
4064                   gdouble          x,
4065                   gdouble          y,
4066                   gdouble          width,
4067                   gdouble          height)
4068 {
4069   GtkStyleContextPrivate *priv;
4070   GtkThemingEngineClass *engine_class;
4071
4072   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4073   g_return_if_fail (cr != NULL);
4074
4075   if (width <= 0 || height <= 0)
4076     return;
4077
4078   priv = context->priv;
4079   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4080
4081   cairo_save (cr);
4082
4083   store_animation_region (context, x, y, width, height);
4084
4085   _gtk_theming_engine_set_context (priv->theming_engine, context);
4086   engine_class->render_focus (priv->theming_engine, cr, x, y, width, height);
4087
4088   cairo_restore (cr);
4089 }
4090
4091 /**
4092  * gtk_render_layout:
4093  * @context: a #GtkStyleContext
4094  * @cr: a #cairo_t
4095  * @x: X origin
4096  * @y: Y origin
4097  * @layout: the #PangoLayout to render
4098  *
4099  * Renders @layout on the coordinates @x, @y
4100  *
4101  * Since: 3.0
4102  **/
4103 void
4104 gtk_render_layout (GtkStyleContext *context,
4105                    cairo_t         *cr,
4106                    gdouble          x,
4107                    gdouble          y,
4108                    PangoLayout     *layout)
4109 {
4110   GtkStyleContextPrivate *priv;
4111   GtkThemingEngineClass *engine_class;
4112   PangoRectangle extents;
4113
4114   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4115   g_return_if_fail (PANGO_IS_LAYOUT (layout));
4116   g_return_if_fail (cr != NULL);
4117
4118   priv = context->priv;
4119   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4120
4121   cairo_save (cr);
4122
4123   pango_layout_get_extents (layout, &extents, NULL);
4124
4125   store_animation_region (context,
4126                           x + extents.x,
4127                           y + extents.y,
4128                           extents.width,
4129                           extents.height);
4130
4131   _gtk_theming_engine_set_context (priv->theming_engine, context);
4132   engine_class->render_layout (priv->theming_engine, cr, x, y, layout);
4133
4134   cairo_restore (cr);
4135 }
4136
4137 /**
4138  * gtk_render_line:
4139  * @context: a #GtkStyleContext
4140  * @cr: a #cairo_t
4141  * @x0: X coordinate for the origin of the line
4142  * @y0: Y coordinate for the origin of the line
4143  * @x1: X coordinate for the end of the line
4144  * @y1: Y coordinate for the end of the line
4145  *
4146  * Renders a line from (x0, y0) to (x1, y1).
4147  *
4148  * Since: 3.0
4149  **/
4150 void
4151 gtk_render_line (GtkStyleContext *context,
4152                  cairo_t         *cr,
4153                  gdouble          x0,
4154                  gdouble          y0,
4155                  gdouble          x1,
4156                  gdouble          y1)
4157 {
4158   GtkStyleContextPrivate *priv;
4159   GtkThemingEngineClass *engine_class;
4160
4161   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4162   g_return_if_fail (cr != NULL);
4163
4164   priv = context->priv;
4165   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4166
4167   cairo_save (cr);
4168
4169   _gtk_theming_engine_set_context (priv->theming_engine, context);
4170   engine_class->render_line (priv->theming_engine, cr, x0, y0, x1, y1);
4171
4172   cairo_restore (cr);
4173 }
4174
4175 /**
4176  * gtk_render_slider:
4177  * @context: a #GtkStyleContext
4178  * @cr: a #cairo_t
4179  * @x: X origin of the rectangle
4180  * @y: Y origin of the rectangle
4181  * @width: rectangle width
4182  * @height: rectangle height
4183  * @orientation: orientation of the slider
4184  *
4185  * Renders a slider (as in #GtkScale) in the rectangle defined by @x, @y,
4186  * @width, @height. @orientation defines whether the slider is vertical
4187  * or horizontal.
4188  *
4189  * <example>
4190  * <title>Typical slider rendering</title>
4191  * <inlinegraphic fileref="sliders.png" format="PNG"/>
4192  * </example>
4193  *
4194  * Since: 3.0
4195  **/
4196 void
4197 gtk_render_slider (GtkStyleContext *context,
4198                    cairo_t         *cr,
4199                    gdouble          x,
4200                    gdouble          y,
4201                    gdouble          width,
4202                    gdouble          height,
4203                    GtkOrientation   orientation)
4204 {
4205   GtkStyleContextPrivate *priv;
4206   GtkThemingEngineClass *engine_class;
4207
4208   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4209   g_return_if_fail (cr != NULL);
4210
4211   if (width <= 0 || height <= 0)
4212     return;
4213
4214   priv = context->priv;
4215   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4216
4217   cairo_save (cr);
4218
4219   store_animation_region (context, x, y, width, height);
4220
4221   _gtk_theming_engine_set_context (priv->theming_engine, context);
4222   engine_class->render_slider (priv->theming_engine, cr, x, y, width, height, orientation);
4223
4224   cairo_restore (cr);
4225 }
4226
4227 /**
4228  * gtk_render_frame_gap:
4229  * @context: a #GtkStyleContext
4230  * @cr: a #cairo_t
4231  * @x: X origin of the rectangle
4232  * @y: Y origin of the rectangle
4233  * @width: rectangle width
4234  * @height: rectangle height
4235  * @gap_side: side where the gap is
4236  * @xy0_gap: initial coordinate (X or Y depending on @gap_side) for the gap
4237  * @xy1_gap: end coordinate (X or Y depending on @gap_side) for the gap
4238  *
4239  * Renders a frame around the rectangle defined by (@x, @y, @width, @height),
4240  * leaving a gap on one side. @xy0_gap and @xy1_gap will mean X coordinates
4241  * for %GTK_POS_TOP and %GTK_POS_BOTTOM gap sides, and Y coordinates for
4242  * %GTK_POS_LEFT and %GTK_POS_RIGHT.
4243  *
4244  * <example>
4245  * <title>Typical rendering of a frame with a gap</title>
4246  * <inlinegraphic fileref="frame-gap.png" format="PNG"/>
4247  * </example>
4248  *
4249  * Since: 3.0
4250  **/
4251 void
4252 gtk_render_frame_gap (GtkStyleContext *context,
4253                       cairo_t         *cr,
4254                       gdouble          x,
4255                       gdouble          y,
4256                       gdouble          width,
4257                       gdouble          height,
4258                       GtkPositionType  gap_side,
4259                       gdouble          xy0_gap,
4260                       gdouble          xy1_gap)
4261 {
4262   GtkStyleContextPrivate *priv;
4263   GtkThemingEngineClass *engine_class;
4264
4265   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4266   g_return_if_fail (cr != NULL);
4267   g_return_if_fail (xy0_gap <= xy1_gap);
4268   g_return_if_fail (xy0_gap >= 0);
4269
4270   if (width <= 0 || height <= 0)
4271     return;
4272
4273   if (gap_side == GTK_POS_LEFT ||
4274       gap_side == GTK_POS_RIGHT)
4275     g_return_if_fail (xy1_gap <= height);
4276   else
4277     g_return_if_fail (xy1_gap <= width);
4278
4279   priv = context->priv;
4280   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4281
4282   cairo_save (cr);
4283
4284   store_animation_region (context, x, y, width, height);
4285
4286   _gtk_theming_engine_set_context (priv->theming_engine, context);
4287   engine_class->render_frame_gap (priv->theming_engine, cr,
4288                                   x, y, width, height, gap_side,
4289                                   xy0_gap, xy1_gap);
4290
4291   cairo_restore (cr);
4292 }
4293
4294 /**
4295  * gtk_render_extension:
4296  * @context: a #GtkStyleContext
4297  * @cr: a #cairo_t
4298  * @x: X origin of the rectangle
4299  * @y: Y origin of the rectangle
4300  * @width: rectangle width
4301  * @height: rectangle height
4302  * @gap_side: side where the gap is
4303  *
4304  * Renders a extension (as in a #GtkNotebook tab) in the rectangle
4305  * defined by @x, @y, @width, @height. The side where the extension
4306  * connects to is defined by @gap_side.
4307  *
4308  * <example>
4309  * <title>Typical extension rendering</title>
4310  * <inlinegraphic fileref="extensions.png" format="PNG"/>
4311  * </example>
4312  *
4313  * Since: 3.0
4314  **/
4315 void
4316 gtk_render_extension (GtkStyleContext *context,
4317                       cairo_t         *cr,
4318                       gdouble          x,
4319                       gdouble          y,
4320                       gdouble          width,
4321                       gdouble          height,
4322                       GtkPositionType  gap_side)
4323 {
4324   GtkStyleContextPrivate *priv;
4325   GtkThemingEngineClass *engine_class;
4326
4327   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4328   g_return_if_fail (cr != NULL);
4329
4330   if (width <= 0 || height <= 0)
4331     return;
4332
4333   priv = context->priv;
4334   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4335
4336   cairo_save (cr);
4337
4338   store_animation_region (context, x, y, width, height);
4339
4340   _gtk_theming_engine_set_context (priv->theming_engine, context);
4341   engine_class->render_extension (priv->theming_engine, cr, x, y, width, height, gap_side);
4342
4343   cairo_restore (cr);
4344 }
4345
4346 /**
4347  * gtk_render_handle:
4348  * @context: a #GtkStyleContext
4349  * @cr: a #cairo_t
4350  * @x: X origin of the rectangle
4351  * @y: Y origin of the rectangle
4352  * @width: rectangle width
4353  * @height: rectangle height
4354  *
4355  * Renders a handle (as in #GtkHandleBox, #GtkPaned and
4356  * #GtkWindow<!-- -->'s resize grip), in the rectangle
4357  * determined by @x, @y, @width, @height.
4358  *
4359  * <example>
4360  * <title>Handles rendered for the paned and grip classes</title>
4361  * <inlinegraphic fileref="handles.png" format="PNG"/>
4362  * </example>
4363  *
4364  * Since: 3.0
4365  **/
4366 void
4367 gtk_render_handle (GtkStyleContext *context,
4368                    cairo_t         *cr,
4369                    gdouble          x,
4370                    gdouble          y,
4371                    gdouble          width,
4372                    gdouble          height)
4373 {
4374   GtkStyleContextPrivate *priv;
4375   GtkThemingEngineClass *engine_class;
4376
4377   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4378   g_return_if_fail (cr != NULL);
4379
4380   if (width <= 0 || height <= 0)
4381     return;
4382
4383   priv = context->priv;
4384   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4385
4386   cairo_save (cr);
4387
4388   store_animation_region (context, x, y, width, height);
4389
4390   _gtk_theming_engine_set_context (priv->theming_engine, context);
4391   engine_class->render_handle (priv->theming_engine, cr, x, y, width, height);
4392
4393   cairo_restore (cr);
4394 }
4395
4396 /**
4397  * gtk_render_activity:
4398  * @context: a #GtkStyleContext
4399  * @cr: a #cairo_t
4400  * @x: X origin of the rectangle
4401  * @y: Y origin of the rectangle
4402  * @width: rectangle width
4403  * @height: rectangle height
4404  *
4405  * Renders an activity area (Such as in #GtkSpinner or the
4406  * fill line in #GtkRange), the state %GTK_STATE_FLAG_ACTIVE
4407  * determines whether there is activity going on.
4408  *
4409  * Since: 3.0
4410  **/
4411 void
4412 gtk_render_activity (GtkStyleContext *context,
4413                      cairo_t         *cr,
4414                      gdouble          x,
4415                      gdouble          y,
4416                      gdouble          width,
4417                      gdouble          height)
4418 {
4419   GtkStyleContextPrivate *priv;
4420   GtkThemingEngineClass *engine_class;
4421
4422   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4423   g_return_if_fail (cr != NULL);
4424
4425   if (width <= 0 || height <= 0)
4426     return;
4427
4428   priv = context->priv;
4429   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4430
4431   cairo_save (cr);
4432
4433   store_animation_region (context, x, y, width, height);
4434
4435   _gtk_theming_engine_set_context (priv->theming_engine, context);
4436   engine_class->render_activity (priv->theming_engine, cr, x, y, width, height);
4437
4438   cairo_restore (cr);
4439 }
4440
4441 /**
4442  * gtk_render_icon_pixbuf:
4443  * @context: a #GtkStyleContext
4444  * @source: the #GtkIconSource specifying the icon to render
4445  * @size: (type int): the size to render the icon at. A size of (GtkIconSize) -1
4446  *        means render at the size of the source and don't scale.
4447  *
4448  * Renders the icon specified by @source at the given @size, returning the result
4449  * in a pixbuf.
4450  *
4451  * Returns: (transfer full): a newly-created #GdkPixbuf containing the rendered icon
4452  *
4453  * Since: 3.0
4454  **/
4455 GdkPixbuf *
4456 gtk_render_icon_pixbuf (GtkStyleContext     *context,
4457                         const GtkIconSource *source,
4458                         GtkIconSize          size)
4459 {
4460   GtkStyleContextPrivate *priv;
4461   GtkThemingEngineClass *engine_class;
4462
4463   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
4464   g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
4465   g_return_val_if_fail (source != NULL, NULL);
4466
4467   priv = context->priv;
4468   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4469
4470   _gtk_theming_engine_set_context (priv->theming_engine, context);
4471   return engine_class->render_icon_pixbuf (priv->theming_engine, source, size);
4472 }
4473
4474 /**
4475  * gtk_render_icon:
4476  * @context: a #GtkStyleContext
4477  * @cr: a #cairo_t
4478  * @pixbuf: a #GdkPixbuf containing the icon to draw
4479  * @x: X position for the @pixbuf
4480  * @y: Y position for the @pixbuf
4481  *
4482  * Renders the icon in @pixbuf at the specified @x and @y coordinates.
4483  *
4484  * Since: 3.2
4485  **/
4486 void
4487 gtk_render_icon (GtkStyleContext *context,
4488                  cairo_t         *cr,
4489                  GdkPixbuf       *pixbuf,
4490                  gdouble          x,
4491                  gdouble          y)
4492 {
4493   GtkStyleContextPrivate *priv;
4494   GtkThemingEngineClass *engine_class;
4495
4496   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4497   g_return_if_fail (cr != NULL);
4498
4499   priv = context->priv;
4500   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
4501
4502   cairo_save (cr);
4503
4504   store_animation_region (context,
4505                           x, y,
4506                           gdk_pixbuf_get_width (pixbuf),
4507                           gdk_pixbuf_get_height (pixbuf));
4508
4509   _gtk_theming_engine_set_context (priv->theming_engine, context);
4510   engine_class->render_icon (priv->theming_engine, cr, pixbuf, x, y);
4511
4512   cairo_restore (cr);
4513 }
4514
4515 static void
4516 draw_insertion_cursor (GtkStyleContext *context,
4517                        cairo_t         *cr,
4518                        gdouble          x,
4519                        gdouble          y,
4520                        gdouble          height,
4521                        gboolean         is_primary,
4522                        PangoDirection   direction,
4523                        gboolean         draw_arrow)
4524
4525 {
4526   GdkRGBA primary_color;
4527   GdkRGBA secondary_color;
4528   gfloat cursor_aspect_ratio;
4529   gint stem_width;
4530   gint offset;
4531
4532   cairo_save (cr);
4533
4534   _gtk_style_context_get_cursor_color (context, &primary_color, &secondary_color);
4535   gdk_cairo_set_source_rgba (cr, is_primary ? &primary_color : &secondary_color);
4536
4537   /* When changing the shape or size of the cursor here,
4538    * propagate the changes to gtktextview.c:text_window_invalidate_cursors().
4539    */
4540
4541   gtk_style_context_get_style (context,
4542                                "cursor-aspect-ratio", &cursor_aspect_ratio,
4543                                NULL);
4544
4545   stem_width = height * cursor_aspect_ratio + 1;
4546
4547   /* put (stem_width % 2) on the proper side of the cursor */
4548   if (direction == PANGO_DIRECTION_LTR)
4549     offset = stem_width / 2;
4550   else
4551     offset = stem_width - stem_width / 2;
4552
4553   cairo_rectangle (cr, x - offset, y, stem_width, height);
4554   cairo_fill (cr);
4555
4556   if (draw_arrow)
4557     {
4558       gint arrow_width;
4559       gint ax, ay;
4560
4561       arrow_width = stem_width + 1;
4562
4563       if (direction == PANGO_DIRECTION_RTL)
4564         {
4565           ax = x - offset - 1;
4566           ay = y + height - arrow_width * 2 - arrow_width + 1;
4567
4568           cairo_move_to (cr, ax, ay + 1);
4569           cairo_line_to (cr, ax - arrow_width, ay + arrow_width);
4570           cairo_line_to (cr, ax, ay + 2 * arrow_width);
4571           cairo_fill (cr);
4572         }
4573       else if (direction == PANGO_DIRECTION_LTR)
4574         {
4575           ax = x + stem_width - offset;
4576           ay = y + height - arrow_width * 2 - arrow_width + 1;
4577
4578           cairo_move_to (cr, ax, ay + 1);
4579           cairo_line_to (cr, ax + arrow_width, ay + arrow_width);
4580           cairo_line_to (cr, ax, ay + 2 * arrow_width);
4581           cairo_fill (cr);
4582         }
4583       else
4584         g_assert_not_reached();
4585     }
4586
4587   cairo_restore (cr);
4588 }
4589
4590 /**
4591  * gtk_render_insertion_cursor:
4592  * @context: a #GtkStyleContext
4593  * @cr: a #cairo_t
4594  * @x: X origin
4595  * @y: Y origin
4596  * @layout: the #PangoLayout of the text
4597  * @index: the index in the #PangoLayout
4598  * @direction: the #PangoDirection of the text
4599  *
4600  * Draws a text caret on @cr at the specified index of @layout.
4601  *
4602  * Since: 3.4
4603  **/
4604 void
4605 gtk_render_insertion_cursor (GtkStyleContext *context,
4606                              cairo_t         *cr,
4607                              gdouble          x,
4608                              gdouble          y,
4609                              PangoLayout     *layout,
4610                              int              index,
4611                              PangoDirection   direction)
4612 {
4613   GtkStyleContextPrivate *priv;
4614   gboolean split_cursor;
4615   PangoRectangle strong_pos, weak_pos;
4616   PangoRectangle *cursor1, *cursor2;
4617   PangoDirection keymap_direction;
4618   PangoDirection direction2;
4619
4620   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
4621   g_return_if_fail (cr != NULL);
4622   g_return_if_fail (PANGO_IS_LAYOUT (layout));
4623   g_return_if_fail (index >= 0);
4624
4625   priv = context->priv;
4626
4627   g_object_get (gtk_settings_get_for_screen (priv->screen),
4628                 "gtk-split-cursor", &split_cursor,
4629                 NULL);
4630
4631   keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gdk_screen_get_display (priv->screen)));
4632
4633   pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
4634
4635   direction2 = PANGO_DIRECTION_NEUTRAL;
4636
4637   if (split_cursor)
4638     {
4639       cursor1 = &strong_pos;
4640
4641       if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y)
4642         {
4643           direction2 = (direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
4644           cursor2 = &weak_pos;
4645         }
4646     }
4647   else
4648     {
4649       if (keymap_direction == direction)
4650         cursor1 = &strong_pos;
4651       else
4652         cursor1 = &weak_pos;
4653     }
4654
4655   draw_insertion_cursor (context,
4656                          cr,
4657                          x + PANGO_PIXELS (cursor1->x),
4658                          y + PANGO_PIXELS (cursor1->y),
4659                          PANGO_PIXELS (cursor1->height),
4660                          TRUE,
4661                          direction,
4662                          direction2 != PANGO_DIRECTION_NEUTRAL);
4663
4664   if (direction2 != PANGO_DIRECTION_NEUTRAL)
4665     {
4666       draw_insertion_cursor (context,
4667                              cr,
4668                              x + PANGO_PIXELS (cursor2->x),
4669                              y + PANGO_PIXELS (cursor2->y),
4670                              PANGO_PIXELS (cursor2->height),
4671                              FALSE,
4672                              direction2,
4673                              TRUE);
4674     }
4675 }
4676
4677 /**
4678  * gtk_draw_insertion_cursor:
4679  * @widget:  a #GtkWidget
4680  * @cr: cairo context to draw to
4681  * @location: location where to draw the cursor (@location->width is ignored)
4682  * @is_primary: if the cursor should be the primary cursor color.
4683  * @direction: whether the cursor is left-to-right or
4684  *             right-to-left. Should never be #GTK_TEXT_DIR_NONE
4685  * @draw_arrow: %TRUE to draw a directional arrow on the
4686  *        cursor. Should be %FALSE unless the cursor is split.
4687  *
4688  * Draws a text caret on @cr at @location. This is not a style function
4689  * but merely a convenience function for drawing the standard cursor shape.
4690  *
4691  * Since: 3.0
4692  * Deprecated: 3.4: Use gtk_render_insertion_cursor() instead.
4693  */
4694 void
4695 gtk_draw_insertion_cursor (GtkWidget          *widget,
4696                            cairo_t            *cr,
4697                            const GdkRectangle *location,
4698                            gboolean            is_primary,
4699                            GtkTextDirection    direction,
4700                            gboolean            draw_arrow)
4701 {
4702   GtkStyleContext *context;
4703
4704   g_return_if_fail (GTK_IS_WIDGET (widget));
4705   g_return_if_fail (cr != NULL);
4706   g_return_if_fail (location != NULL);
4707   g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
4708
4709   context = gtk_widget_get_style_context (widget);
4710
4711   draw_insertion_cursor (context, cr,
4712                          location->x, location->y, location->height,
4713                          is_primary,
4714                          (direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR,
4715                          draw_arrow);
4716 }
4717
4718 static AtkAttributeSet *
4719 add_attribute (AtkAttributeSet  *attributes,
4720                AtkTextAttribute  attr,
4721                const gchar      *value)
4722 {
4723   AtkAttribute *at;
4724
4725   at = g_new (AtkAttribute, 1);
4726   at->name = g_strdup (atk_text_attribute_get_name (attr));
4727   at->value = g_strdup (value);
4728
4729   return g_slist_prepend (attributes, at);
4730 }
4731
4732 /*
4733  * _gtk_style_context_get_attributes:
4734  * @attributes: a #AtkAttributeSet to add attributes to
4735  * @context: the #GtkStyleContext to get attributes from
4736  * @flags: the state to use with @context
4737  *
4738  * Adds the foreground and background color from @context to
4739  * @attributes, after translating them to ATK attributes.
4740  *
4741  * This is a convenience function that can be used in
4742  * implementing the #AtkText interface in widgets.
4743  *
4744  * Returns: the modified #AtkAttributeSet
4745  */
4746 AtkAttributeSet *
4747 _gtk_style_context_get_attributes (AtkAttributeSet *attributes,
4748                                    GtkStyleContext *context,
4749                                    GtkStateFlags    flags)
4750 {
4751   GdkRGBA color;
4752   gchar *value;
4753
4754   gtk_style_context_get_background_color (context, flags, &color);
4755   value = g_strdup_printf ("%u,%u,%u",
4756                            (guint) ceil (color.red * 65536 - color.red),
4757                            (guint) ceil (color.green * 65536 - color.green),
4758                            (guint) ceil (color.blue * 65536 - color.blue));
4759   attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
4760   g_free (value);
4761
4762   gtk_style_context_get_color (context, flags, &color);
4763   value = g_strdup_printf ("%u,%u,%u",
4764                            (guint) ceil (color.red * 65536 - color.red),
4765                            (guint) ceil (color.green * 65536 - color.green),
4766                            (guint) ceil (color.blue * 65536 - color.blue));
4767   attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
4768   g_free (value);
4769
4770   return attributes;
4771 }