]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkstylecontext.c
filechooser: Also convert get_uris() to returning native paths
[~andy/gtk] / gtk / gtkstylecontext.c
index 2d8023c298e701d215f1037800d524bfcbfbf059..f5d1e86ea5a888f1a11729c50a188ad0b805d756 100644 (file)
 #include <gobject/gvaluecollector.h>
 
 #include "gtkstylecontextprivate.h"
+#include "gtkcontainerprivate.h"
+#include "gtkcsscolorvalueprivate.h"
+#include "gtkcsscornervalueprivate.h"
 #include "gtkcssenginevalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
 #include "gtkcssrgbavalueprivate.h"
+#include "gtkdebug.h"
 #include "gtkstylepropertiesprivate.h"
 #include "gtktypebuiltins.h"
 #include "gtkthemingengineprivate.h"
 #include "gtkwidget.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
-#include "gtksymboliccolorprivate.h"
-#include "gtkcssnumbervalueprivate.h"
 #include "gtkiconfactory.h"
 #include "gtkwidgetpath.h"
 #include "gtkwidgetprivate.h"
 #include "gtkstylecascadeprivate.h"
 #include "gtkstyleproviderprivate.h"
 #include "gtksettings.h"
+#include "gtksettingsprivate.h"
+
+#include "deprecated/gtkgradientprivate.h"
+#include "deprecated/gtksymboliccolorprivate.h"
 
 /**
  * SECTION:gtkstylecontext
 
 /* When these change we do a full restyling. Otherwise we try to figure out
  * if we need to change things. */
-#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS)
+#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE)
 /* When these change we don't clear the cache. This takes more memory but makes
  * things go faster. */
 #define GTK_STYLE_CONTEXT_CACHED_CHANGE (GTK_CSS_CHANGE_STATE)
@@ -330,6 +337,7 @@ struct PropertyValue
 
 struct GtkStyleInfo
 {
+  GtkStyleInfo *next;
   GArray *style_classes;
   GArray *regions;
   GtkJunctionSides junction_sides;
@@ -341,6 +349,7 @@ struct StyleData
 {
   GtkCssComputedValues *store;
   GArray *property_cache;
+  guint ref_count;
 };
 
 struct _GtkStyleContextPrivate
@@ -351,17 +360,19 @@ struct _GtkStyleContextPrivate
 
   GtkStyleContext *parent;
   GSList *children;
-  GtkWidget *widget;            
+  GtkWidget *widget;
   GtkWidgetPath *widget_path;
   GHashTable *style_data;
-  GSList *info_stack;
+  GtkStyleInfo *info;
 
-  GtkTextDirection direction;
+  GdkFrameClock *frame_clock;
+  guint frame_clock_update_id;
 
   GtkCssChange relevant_changes;
   GtkCssChange pending_changes;
 
-  guint invalidating_context : 1;
+  const GtkBitmask *invalidating_context;
+  guint animating : 1;
   guint invalid : 1;
 };
 
@@ -369,6 +380,7 @@ enum {
   PROP_0,
   PROP_SCREEN,
   PROP_DIRECTION,
+  PROP_FRAME_CLOCK,
   PROP_PARENT
 };
 
@@ -389,11 +401,12 @@ static void gtk_style_context_impl_get_property (GObject      *object,
                                                  guint         prop_id,
                                                  GValue       *value,
                                                  GParamSpec   *pspec);
-static GtkSymbolicColor *
-            gtk_style_context_color_lookup_func (gpointer      contextp,
-                                                 const char   *name);
+static StyleData *style_data_lookup             (GtkStyleContext *context);
 
 
+static void gtk_style_context_disconnect_update (GtkStyleContext *context);
+static void gtk_style_context_connect_update    (GtkStyleContext *context);
+
 G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
 
 static void
@@ -432,6 +445,13 @@ gtk_style_context_class_init (GtkStyleContextClass *klass)
                                                         P_("The associated GdkScreen"),
                                                         GDK_TYPE_SCREEN,
                                                         GTK_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_FRAME_CLOCK,
+                                   g_param_spec_object ("paint-clock",
+                                                        P_("FrameClock"),
+                                                        P_("The associated GdkFrameClock"),
+                                                        GDK_TYPE_FRAME_CLOCK,
+                                                        GTK_PARAM_READWRITE));
   g_object_class_install_property (object_class,
                                    PROP_DIRECTION,
                                    g_param_spec_enum ("direction",
@@ -459,6 +479,65 @@ gtk_style_context_class_init (GtkStyleContextClass *klass)
   g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
 }
 
+static StyleData *
+style_data_new (void)
+{
+  StyleData *data;
+
+  data = g_slice_new0 (StyleData);
+  data->ref_count = 1;
+
+  return data;
+}
+
+static void
+clear_property_cache (StyleData *data)
+{
+  guint i;
+
+  if (!data->property_cache)
+    return;
+
+  for (i = 0; i < data->property_cache->len; i++)
+    {
+      PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);
+
+      g_param_spec_unref (node->pspec);
+      g_value_unset (&node->value);
+    }
+
+  g_array_free (data->property_cache, TRUE);
+  data->property_cache = NULL;
+}
+
+static StyleData *
+style_data_ref (StyleData *style_data)
+{
+  style_data->ref_count++;
+
+  return style_data;
+}
+
+static void
+style_data_unref (StyleData *data)
+{
+  data->ref_count--;
+
+  if (data->ref_count > 0)
+    return;
+
+  g_object_unref (data->store);
+  clear_property_cache (data);
+
+  g_slice_free (StyleData, data);
+}
+
+static gboolean
+style_data_is_animating (StyleData *style_data)
+{
+  return !_gtk_css_computed_values_is_static (style_data->store);
+}
+
 static GtkStyleInfo *
 style_info_new (void)
 {
@@ -471,16 +550,43 @@ style_info_new (void)
   return info;
 }
 
+static void
+style_info_set_data (GtkStyleInfo *info,
+                     StyleData    *data)
+{
+  if (info->data == data)
+    return;
+
+  if (data)
+    style_data_ref (data);
+
+  if (info->data)
+    style_data_unref (info->data);
+
+  info->data = data;
+}
+
 static void
 style_info_free (GtkStyleInfo *info)
 {
+  style_info_set_data (info, NULL);
   g_array_free (info->style_classes, TRUE);
   g_array_free (info->regions, TRUE);
   g_slice_free (GtkStyleInfo, info);
 }
 
 static GtkStyleInfo *
-style_info_copy (const GtkStyleInfo *info)
+style_info_pop (GtkStyleInfo *info)
+{
+  GtkStyleInfo *next = info->next;
+
+  style_info_free (info);
+
+  return next;
+}
+
+static GtkStyleInfo *
+style_info_copy (GtkStyleInfo *info)
 {
   GtkStyleInfo *copy;
 
@@ -493,9 +599,10 @@ style_info_copy (const GtkStyleInfo *info)
                        info->regions->data,
                        info->regions->len);
 
+  copy->next = info;
   copy->junction_sides = info->junction_sides;
   copy->state_flags = info->state_flags;
-  copy->data = info->data;
+  style_info_set_data (copy, info->data);
 
   return copy;
 }
@@ -561,50 +668,51 @@ style_info_equal (gconstpointer elem1,
   return TRUE;
 }
 
-static StyleData *
-style_data_new (void)
+static void
+gtk_style_context_cascade_changed (GtkStyleCascade *cascade,
+                                   GtkStyleContext *context)
 {
-  StyleData *data;
-
-  data = g_slice_new0 (StyleData);
-
-  return data;
+  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_SOURCE);
 }
 
 static void
-clear_property_cache (StyleData *data)
+gtk_style_context_set_cascade (GtkStyleContext *context,
+                               GtkStyleCascade *cascade)
 {
-  guint i;
+  GtkStyleContextPrivate *priv;
 
-  if (!data->property_cache)
+  priv = context->priv;
+
+  if (priv->cascade == cascade)
     return;
 
-  for (i = 0; i < data->property_cache->len; i++)
+  if (cascade)
     {
-      PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);
-
-      g_param_spec_unref (node->pspec);
-      g_value_unset (&node->value);
+      g_object_ref (cascade);
+      g_signal_connect (cascade,
+                        "-gtk-private-changed",
+                        G_CALLBACK (gtk_style_context_cascade_changed),
+                        context);
     }
 
-  g_array_free (data->property_cache, TRUE);
-  data->property_cache = NULL;
-}
+  if (priv->cascade)
+    {
+      g_signal_handlers_disconnect_by_func (priv->cascade, 
+                                            gtk_style_context_cascade_changed,
+                                            context);
+      g_object_unref (priv->cascade);
+    }
 
-static void
-style_data_free (StyleData *data)
-{
-  g_object_unref (data->store);
-  clear_property_cache (data);
+  priv->cascade = cascade;
 
-  g_slice_free (StyleData, data);
+  if (cascade)
+    gtk_style_context_cascade_changed (cascade, context);
 }
 
 static void
 gtk_style_context_init (GtkStyleContext *style_context)
 {
   GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
 
   priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
                                                             GTK_TYPE_STYLE_CONTEXT,
@@ -613,18 +721,122 @@ gtk_style_context_init (GtkStyleContext *style_context)
   priv->style_data = g_hash_table_new_full (style_info_hash,
                                             style_info_equal,
                                             (GDestroyNotify) style_info_free,
-                                            (GDestroyNotify) style_data_free);
-
-  priv->direction = GTK_TEXT_DIR_LTR;
+                                            (GDestroyNotify) style_data_unref);
 
   priv->screen = gdk_screen_get_default ();
-  priv->cascade = _gtk_style_cascade_get_for_screen (priv->screen);
-  g_object_ref (priv->cascade);
   priv->relevant_changes = GTK_CSS_CHANGE_ANY;
 
   /* Create default info store */
-  info = style_info_new ();
-  priv->info_stack = g_slist_prepend (priv->info_stack, info);
+  priv->info = style_info_new ();
+  priv->info->state_flags = GTK_STATE_FLAG_DIR_LTR;
+
+  gtk_style_context_set_cascade (style_context,
+                                 _gtk_style_cascade_get_for_screen (priv->screen));
+}
+
+static void
+gtk_style_context_update (GdkFrameClock  *clock,
+                          GtkStyleContext *context)
+{
+  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
+}
+
+static gboolean
+gtk_style_context_is_animating (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  return priv->animating;
+}
+
+static void
+gtk_style_context_disconnect_update (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  if (priv->frame_clock && priv->frame_clock_update_id)
+    {
+      g_signal_handler_disconnect (priv->frame_clock,
+                                   priv->frame_clock_update_id);
+      priv->frame_clock_update_id = 0;
+      gdk_frame_clock_end_updating (priv->frame_clock);
+    }
+}
+
+static void
+gtk_style_context_connect_update (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  if (priv->frame_clock && priv->frame_clock_update_id == 0)
+    {
+      priv->frame_clock_update_id = g_signal_connect (priv->frame_clock,
+                                                      "update",
+                                                      G_CALLBACK (gtk_style_context_update),
+                                                      context);
+      gdk_frame_clock_begin_updating (priv->frame_clock);
+    }
+}
+
+static void
+gtk_style_context_stop_animating (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  if (!gtk_style_context_is_animating (context))
+    return;
+
+  priv->animating = FALSE;
+
+  gtk_style_context_disconnect_update (context);
+}
+
+static void
+gtk_style_context_start_animating (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  if (gtk_style_context_is_animating (context))
+    return;
+
+  priv->animating = TRUE;
+
+  gtk_style_context_connect_update (context);
+}
+
+static gboolean
+gtk_style_context_should_animate (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  gboolean animate;
+
+  priv = context->priv;
+
+  if (priv->widget == NULL)
+    return FALSE;
+
+  if (!gtk_widget_get_mapped (priv->widget))
+    return FALSE;
+
+  data = style_data_lookup (context);
+  if (!style_data_is_animating (data))
+    return FALSE;
+
+  g_object_get (gtk_widget_get_settings (context->priv->widget),
+                "gtk-enable-animations", &animate,
+                NULL);
+
+  return animate;
+}
+
+void
+_gtk_style_context_update_animating (GtkStyleContext *context)
+{
+  if (gtk_style_context_should_animate (context))
+    gtk_style_context_start_animating (context);
+  else
+    gtk_style_context_stop_animating (context);
 }
 
 static void
@@ -636,19 +848,22 @@ gtk_style_context_finalize (GObject *object)
   style_context = GTK_STYLE_CONTEXT (object);
   priv = style_context->priv;
 
+  gtk_style_context_stop_animating (style_context);
+
   /* children hold a reference to us */
   g_assert (priv->children == NULL);
 
   gtk_style_context_set_parent (style_context, NULL);
 
+  gtk_style_context_set_cascade (style_context, NULL);
+
   if (priv->widget_path)
     gtk_widget_path_free (priv->widget_path);
 
   g_hash_table_destroy (priv->style_data);
 
-  g_object_unref (priv->cascade);
-
-  g_slist_free_full (priv->info_stack, (GDestroyNotify) style_info_free);
+  while (priv->info)
+    priv->info = style_info_pop (priv->info);
 
   G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
 }
@@ -670,8 +885,14 @@ gtk_style_context_impl_set_property (GObject      *object,
                                     g_value_get_object (value));
       break;
     case PROP_DIRECTION:
+      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
       gtk_style_context_set_direction (style_context,
                                        g_value_get_enum (value));
+      G_GNUC_END_IGNORE_DEPRECATIONS;
+      break;
+    case PROP_FRAME_CLOCK:
+      gtk_style_context_set_frame_clock (style_context,
+                                         g_value_get_object (value));
       break;
     case PROP_PARENT:
       gtk_style_context_set_parent (style_context,
@@ -701,7 +922,12 @@ gtk_style_context_impl_get_property (GObject    *object,
       g_value_set_object (value, priv->screen);
       break;
     case PROP_DIRECTION:
-      g_value_set_enum (value, priv->direction);
+      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+      g_value_set_enum (value, gtk_style_context_get_direction (style_context));
+      G_GNUC_END_IGNORE_DEPRECATIONS;
+      break;
+    case PROP_FRAME_CLOCK:
+      g_value_set_object (value, priv->frame_clock);
       break;
     case PROP_PARENT:
       g_value_set_object (value, priv->parent);
@@ -712,44 +938,18 @@ gtk_style_context_impl_get_property (GObject    *object,
     }
 }
 
-static void
-build_properties (GtkStyleContext *context,
-                  StyleData       *style_data,
-                  GtkWidgetPath   *path,
-                  GtkStateFlags    state)
-{
-  GtkStyleContextPrivate *priv;
-  GtkCssMatcher matcher;
-  GtkCssLookup *lookup;
-
-  priv = context->priv;
-
-  _gtk_css_matcher_init (&matcher, path, state);
-  lookup = _gtk_css_lookup_new ();
-
-  _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
-                                      &matcher,
-                                      lookup);
-
-  style_data->store = _gtk_css_computed_values_new ();
-  _gtk_css_lookup_resolve (lookup, context, style_data->store);
-  _gtk_css_lookup_free (lookup);
-}
-
 static GtkWidgetPath *
-create_query_path (GtkStyleContext *context)
+create_query_path (GtkStyleContext *context,
+                   GtkStyleInfo    *info)
 {
   GtkStyleContextPrivate *priv;
   GtkWidgetPath *path;
-  GtkStyleInfo *info;
   guint i, pos;
 
   priv = context->priv;
   path = priv->widget ? _gtk_widget_create_path (priv->widget) : gtk_widget_path_copy (priv->widget_path);
   pos = gtk_widget_path_length (path) - 1;
 
-  info = priv->info_stack->data;
-
   /* Set widget regions */
   for (i = 0; i < info->regions->len; i++)
     {
@@ -774,14 +974,45 @@ create_query_path (GtkStyleContext *context)
   return path;
 }
 
+static void
+build_properties (GtkStyleContext      *context,
+                  GtkCssComputedValues *values,
+                  GtkStyleInfo         *info,
+                  const GtkBitmask     *relevant_changes)
+{
+  GtkStyleContextPrivate *priv;
+  GtkCssMatcher matcher;
+  GtkWidgetPath *path;
+  GtkCssLookup *lookup;
+
+  priv = context->priv;
+
+  path = create_query_path (context, info);
+  lookup = _gtk_css_lookup_new (relevant_changes);
+
+  if (_gtk_css_matcher_init (&matcher, path, info->state_flags))
+    _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                                        &matcher,
+                                        lookup);
+
+  _gtk_css_lookup_resolve (lookup, 
+                           GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                           values,
+                           priv->parent ? style_data_lookup (priv->parent)->store : NULL);
+
+  _gtk_css_lookup_free (lookup);
+  gtk_widget_path_free (path);
+}
+
 static StyleData *
 style_data_lookup (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
   GtkStyleInfo *info;
+  StyleData *data;
 
   priv = context->priv;
-  info = priv->info_stack->data;
+  info = priv->info;
 
   /* Current data in use is cached, just return it */
   if (info->data)
@@ -789,25 +1020,40 @@ style_data_lookup (GtkStyleContext *context)
 
   g_assert (priv->widget != NULL || priv->widget_path != NULL);
 
-  info->data = g_hash_table_lookup (priv->style_data, info);
-
-  if (!info->data)
+  data = g_hash_table_lookup (priv->style_data, info);
+  if (data)
     {
-      GtkWidgetPath *path;
+      style_info_set_data (info, data);
+      return data;
+    }
 
-      path = create_query_path (context);
+  data = style_data_new ();
+  data->store = _gtk_css_computed_values_new ();
+  style_info_set_data (info, data);
+  g_hash_table_insert (priv->style_data,
+                       style_info_copy (info),
+                       data);
 
-      info->data = style_data_new ();
-      g_hash_table_insert (priv->style_data,
-                           style_info_copy (info),
-                           info->data);
+  build_properties (context, data->store, info, NULL);
 
-      build_properties (context, info->data, path, info->state_flags);
+  return data;
+}
 
-      gtk_widget_path_free (path);
-    }
+static StyleData *
+style_data_lookup_for_state (GtkStyleContext *context,
+                             GtkStateFlags    state)
+{
+  StyleData *data;
+
+  if (context->priv->info->state_flags == state)
+    return style_data_lookup (context);
 
-  return info->data;
+  gtk_style_context_save (context);
+  gtk_style_context_set_state (context, state);
+  data = style_data_lookup (context);
+  gtk_style_context_restore (context);
+
+  return data;
 }
 
 static void
@@ -827,8 +1073,8 @@ gtk_style_context_set_invalid (GtkStyleContext *context,
     {
       if (priv->parent)
         gtk_style_context_set_invalid (priv->parent, TRUE);
-      else if (priv->widget)
-        gtk_widget_queue_resize (priv->widget);
+      else if (GTK_IS_RESIZE_CONTAINER (priv->widget))
+        _gtk_container_queue_restyle (GTK_CONTAINER (priv->widget));
     }
 }
 
@@ -840,7 +1086,7 @@ gtk_style_context_set_invalid (GtkStyleContext *context,
 static gboolean
 gtk_style_context_is_saved (GtkStyleContext *context)
 {
-  return context->priv->info_stack->next != NULL;
+  return context->priv->info->next != NULL;
 }
 
 static void
@@ -848,15 +1094,15 @@ gtk_style_context_queue_invalidate_internal (GtkStyleContext *context,
                                              GtkCssChange     change)
 {
   GtkStyleContextPrivate *priv = context->priv;
-  GtkStyleInfo *info = priv->info_stack->data;
+  GtkStyleInfo *info = priv->info;
 
   if (gtk_style_context_is_saved (context))
     {
-      info->data = NULL;
+      style_info_set_data (info, NULL);
     }
   else
     {
-      _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_STATE);
+      _gtk_style_context_queue_invalidate (context, change);
       /* XXX: We need to invalidate siblings here somehow */
     }
 }
@@ -892,6 +1138,8 @@ _gtk_style_context_set_widget (GtkStyleContext *context,
 
   context->priv->widget = widget;
 
+  _gtk_style_context_update_animating (context);
+
   _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
 }
 
@@ -906,6 +1154,10 @@ _gtk_style_context_set_widget (GtkStyleContext *context,
  *            %GTK_STYLE_PROVIDER_PRIORITY_USER
  *
  * Adds a style provider to @context, to be used in style construction.
+ * Note that a style provider added by this function only affects
+ * the style of the widget to which @context belongs. If you want
+ * to affect the style of all widgets, use
+ * gtk_style_context_add_provider_for_screen().
  *
  * <note><para>If both priorities are the same, A #GtkStyleProvider
  * added through this function takes precedence over another added
@@ -931,13 +1183,14 @@ gtk_style_context_add_provider (GtkStyleContext  *context,
       
       new_cascade = _gtk_style_cascade_new ();
       _gtk_style_cascade_set_parent (new_cascade, priv->cascade);
-      g_object_unref (priv->cascade);
-      priv->cascade = new_cascade;
+      _gtk_style_cascade_add_provider (new_cascade, provider, priority);
+      gtk_style_context_set_cascade (context, new_cascade);
+      g_object_unref (new_cascade);
+    }
+  else
+    {
+      _gtk_style_cascade_add_provider (priv->cascade, provider, priority);
     }
-
-  _gtk_style_cascade_add_provider (priv->cascade, provider, priority);
-
-  gtk_style_context_invalidate (context);
 }
 
 /**
@@ -1011,8 +1264,7 @@ gtk_style_context_reset_widgets (GdkScreen *screen)
  *            %GTK_STYLE_PROVIDER_PRIORITY_USER
  *
  * Adds a global style provider to @screen, which will be used
- * in style construction for all #GtkStyleContext<!-- -->s under
- * @screen.
+ * in style construction for all #GtkStyleContexts under @screen.
  *
  * GTK+ uses this to make styling information from #GtkSettings
  * available.
@@ -1032,6 +1284,7 @@ gtk_style_context_add_provider_for_screen (GdkScreen        *screen,
 
   g_return_if_fail (GDK_IS_SCREEN (screen));
   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+  g_return_if_fail (!GTK_IS_SETTINGS (provider) || _gtk_settings_get_screen (GTK_SETTINGS (provider)) == screen);
 
   cascade = _gtk_style_cascade_get_for_screen (screen);
   _gtk_style_cascade_add_provider (cascade, provider, priority);
@@ -1054,6 +1307,7 @@ gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
 
   g_return_if_fail (GDK_IS_SCREEN (screen));
   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+  g_return_if_fail (!GTK_IS_SETTINGS (provider));
 
   cascade = _gtk_style_cascade_get_for_screen (screen);
   _gtk_style_cascade_remove_provider (cascade, provider);
@@ -1151,11 +1405,8 @@ gtk_style_context_get_property (GtkStyleContext *context,
       return;
     }
 
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  data = style_data_lookup (context);
+  data = style_data_lookup_for_state (context, state);
   _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
-  gtk_style_context_restore (context);
 }
 
 /**
@@ -1242,14 +1493,18 @@ void
 gtk_style_context_set_state (GtkStyleContext *context,
                              GtkStateFlags    flags)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
+  GtkStateFlags old_flags;
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-  info->state_flags = flags;
+  old_flags = context->priv->info->state_flags;
+  if (old_flags == flags)
+    return;
+
+  context->priv->info->state_flags = flags;
+
+  if (((old_flags ^ flags) & (GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL)) &&
+      !gtk_style_context_is_saved (context))
+    g_object_notify (G_OBJECT (context), "direction");
   
   gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
 }
@@ -1267,15 +1522,9 @@ gtk_style_context_set_state (GtkStyleContext *context,
 GtkStateFlags
 gtk_style_context_get_state (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-
-  return info->state_flags;
+  return context->priv->info->state_flags;
 }
 
 /**
@@ -1454,16 +1703,17 @@ void
 gtk_style_context_save (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-
-  info = style_info_copy (priv->info_stack->data);
-  priv->info_stack = g_slist_prepend (priv->info_stack, info);
+  priv->info = style_info_copy (priv->info);
+  /* Need to unset animations here because we can not know what style
+   * class potential transitions came from once we save().
+   */
+  if (priv->info->data && style_data_is_animating (priv->info->data))
+    style_info_set_data (priv->info, NULL);
 }
 
 /**
@@ -1479,26 +1729,19 @@ void
 gtk_style_context_restore (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
   priv = context->priv;
 
-  if (priv->info_stack)
-    {
-      info = priv->info_stack->data;
-      priv->info_stack = g_slist_remove (priv->info_stack, info);
-      style_info_free (info);
-    }
+  priv->info = style_info_pop (priv->info);
 
-  if (!priv->info_stack)
+  if (!priv->info)
     {
       g_warning ("Unpaired gtk_style_context_restore() call");
 
       /* Create default region */
-      info = style_info_new ();
-      priv->info_stack = g_slist_prepend (priv->info_stack, info);
+      priv->info = style_info_new ();
     }
 }
 
@@ -1633,8 +1876,7 @@ gtk_style_context_add_class (GtkStyleContext *context,
   priv = context->priv;
   class_quark = g_quark_from_string (class_name);
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (!style_class_find (info->style_classes, class_quark, &position))
     {
@@ -1672,8 +1914,7 @@ gtk_style_context_remove_class (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (style_class_find (info->style_classes, class_quark, &position))
     {
@@ -1713,8 +1954,7 @@ gtk_style_context_has_class (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (style_class_find (info->style_classes, class_quark, NULL))
     return TRUE;
@@ -1747,8 +1987,7 @@ gtk_style_context_list_classes (GtkStyleContext *context)
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   for (i = 0; i < info->style_classes->len; i++)
     {
@@ -1786,8 +2025,7 @@ gtk_style_context_list_regions (GtkStyleContext *context)
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   for (i = 0; i < info->regions->len; i++)
     {
@@ -1871,8 +2109,7 @@ gtk_style_context_add_region (GtkStyleContext *context,
   priv = context->priv;
   region_quark = g_quark_from_string (region_name);
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (!region_find (info->regions, region_quark, &position))
     {
@@ -1915,8 +2152,7 @@ gtk_style_context_remove_region (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (region_find (info->regions, region_quark, &position))
     {
@@ -1963,8 +2199,7 @@ gtk_style_context_has_region (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (region_find (info->regions, region_quark, &position))
     {
@@ -2009,17 +2244,6 @@ _gtk_style_context_peek_property (GtkStyleContext *context,
   return _gtk_css_computed_values_get_value (data->store, property_id);
 }
 
-double
-_gtk_style_context_get_number (GtkStyleContext *context,
-                               guint            property_id,
-                               double           one_hundred_percent)
-{
-  GtkCssValue *value;
-  
-  value = _gtk_style_context_peek_property (context, property_id);
-  return _gtk_css_number_value_get (value, one_hundred_percent);
-}
-
 const GValue *
 _gtk_style_context_peek_style_property (GtkStyleContext *context,
                                         GType            widget_type,
@@ -2033,10 +2257,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
 
   priv = context->priv;
 
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  data = style_data_lookup (context);
-  gtk_style_context_restore (context);
+  data = style_data_lookup_for_state (context, state);
 
   key.widget_type = widget_type;
   key.state = state;
@@ -2074,6 +2295,8 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
                                                  widget_path,
                                                  state, pspec, &pcache->value))
         {
+          G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+
           /* Resolve symbolic colors to GdkColor/GdkRGBA */
           if (G_VALUE_TYPE (&pcache->value) == GTK_TYPE_SYMBOLIC_COLOR)
             {
@@ -2089,7 +2312,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
               else
                 g_value_init (&pcache->value, GDK_TYPE_COLOR);
 
-              if (_gtk_style_context_resolve_color (context, color, &rgba))
+              if (_gtk_style_context_resolve_color (context, _gtk_symbolic_color_get_css_value (color), &rgba, NULL))
                 {
                   if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
                     g_value_set_boxed (&pcache->value, &rgba);
@@ -2110,6 +2333,8 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
               gtk_symbolic_color_unref (color);
             }
 
+          G_GNUC_END_IGNORE_DEPRECATIONS;
+
           if (priv->widget)
             gtk_widget_path_free (widget_path);
 
@@ -2369,9 +2594,7 @@ gtk_style_context_set_screen (GtkStyleContext *context,
 
   if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
     {
-      g_object_unref (priv->cascade);
-      priv->cascade = _gtk_style_cascade_get_for_screen (screen);
-      g_object_ref (priv->cascade);
+      gtk_style_context_set_cascade (context, _gtk_style_cascade_get_for_screen (screen));
     }
   else
     {
@@ -2381,8 +2604,6 @@ gtk_style_context_set_screen (GtkStyleContext *context,
   priv->screen = screen;
 
   g_object_notify (G_OBJECT (context), "screen");
-
-  gtk_style_context_invalidate (context);
 }
 
 /**
@@ -2404,6 +2625,70 @@ gtk_style_context_get_screen (GtkStyleContext *context)
   return priv->screen;
 }
 
+/**
+ * gtk_style_context_set_frame_clock:
+ * @context: a #GdkFrameClock
+ * @frame_clock: a #GdkFrameClock
+ *
+ * Attaches @context to the given frame clock.
+ *
+ * The frame clock is used for the timing of animations.
+ *
+ * If you are using a #GtkStyleContext returned from
+ * gtk_widget_get_style_context(), you do not need to
+ * call this yourself.
+ *
+ * Since: 3.8
+ **/
+void
+gtk_style_context_set_frame_clock (GtkStyleContext *context,
+                                   GdkFrameClock   *frame_clock)
+{
+  GtkStyleContextPrivate *priv;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock));
+
+  priv = context->priv;
+  if (priv->frame_clock == frame_clock)
+    return;
+
+  if (priv->animating)
+    gtk_style_context_disconnect_update (context);
+
+  if (priv->frame_clock)
+    g_object_unref (priv->frame_clock);
+  priv->frame_clock = frame_clock;
+  if (priv->frame_clock)
+    g_object_ref (priv->frame_clock);
+
+  if (priv->animating)
+    gtk_style_context_connect_update (context);
+
+  g_object_notify (G_OBJECT (context), "paint-clock");
+}
+
+/**
+ * gtk_style_context_get_frame_clock:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the #GdkFrameClock to which @context is attached.
+ *
+ * Returns: (transfer none): a #GdkFrameClock, or %NULL
+ *  if @context does not have an attached frame clock.
+ * Since: 3.8
+ **/
+GdkFrameClock *
+gtk_style_context_get_frame_clock (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+  priv = context->priv;
+  return priv->frame_clock;
+}
+
 /**
  * gtk_style_context_set_direction:
  * @context: a #GtkStyleContext
@@ -2416,19 +2701,38 @@ gtk_style_context_get_screen (GtkStyleContext *context)
  * call this yourself.
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.8: Use gtk_style_context_set_state() with
+ *   #GTK_STATE_FLAG_DIR_LTR and #GTK_STATE_FLAG_DIR_RTL
+ *   instead.
  **/
 void
 gtk_style_context_set_direction (GtkStyleContext  *context,
                                  GtkTextDirection  direction)
 {
-  GtkStyleContextPrivate *priv;
+  GtkStateFlags state;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  priv = context->priv;
-  priv->direction = direction;
+  state = gtk_style_context_get_state (context);
+  state &= ~(GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL);
+
+  switch (direction)
+    {
+    case GTK_TEXT_DIR_LTR:
+      state |= GTK_STATE_FLAG_DIR_LTR;
+      break;
+
+    case GTK_TEXT_DIR_RTL:
+      state |= GTK_STATE_FLAG_DIR_RTL;
+      break;
+
+    case GTK_TEXT_DIR_NONE:
+    default:
+      break;
+    }
 
-  g_object_notify (G_OBJECT (context), "direction");
+  gtk_style_context_set_state (context, state);
 }
 
 /**
@@ -2440,16 +2744,26 @@ gtk_style_context_set_direction (GtkStyleContext  *context,
  * Returns: the widget direction
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.8: Use gtk_style_context_get_state() and
+ *   check for #GTK_STATE_FLAG_DIR_LTR and
+ *   #GTK_STATE_FLAG_DIR_RTL instead.
  **/
 GtkTextDirection
 gtk_style_context_get_direction (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
+  GtkStateFlags state;
 
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), GTK_TEXT_DIR_LTR);
 
-  priv = context->priv;
-  return priv->direction;
+  state = gtk_style_context_get_state (context);
+
+  if (state & GTK_STATE_FLAG_DIR_LTR)
+    return GTK_TEXT_DIR_LTR;
+  else if (state & GTK_STATE_FLAG_DIR_RTL)
+    return GTK_TEXT_DIR_RTL;
+  else
+    return GTK_TEXT_DIR_NONE;
 }
 
 /**
@@ -2474,14 +2788,9 @@ void
 gtk_style_context_set_junction_sides (GtkStyleContext  *context,
                                       GtkJunctionSides  sides)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-  info->junction_sides = sides;
+  context->priv->info->junction_sides = sides;
 }
 
 /**
@@ -2497,45 +2806,16 @@ gtk_style_context_set_junction_sides (GtkStyleContext  *context,
 GtkJunctionSides
 gtk_style_context_get_junction_sides (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-  return info->junction_sides;
-}
-
-static GtkSymbolicColor *
-gtk_style_context_color_lookup_func (gpointer    contextp,
-                                     const char *name)
-{
-  GtkStyleContext *context = contextp;
-
-  return _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade), name);
-}
-
-GtkCssValue *
-_gtk_style_context_resolve_color_value (GtkStyleContext  *context,
-                                        GtkCssValue      *current,
-                                       GtkCssValue      *color)
-{
-  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
-  g_return_val_if_fail (current != NULL, FALSE);
-  g_return_val_if_fail (color != NULL, FALSE);
-
-  return _gtk_symbolic_color_resolve_full ((GtkSymbolicColor *) color,
-                                           current,
-                                           gtk_style_context_color_lookup_func,
-                                           context);
+  return context->priv->info->junction_sides;
 }
 
-
 gboolean
-_gtk_style_context_resolve_color (GtkStyleContext  *context,
-                                  GtkSymbolicColor *color,
-                                  GdkRGBA          *result)
+_gtk_style_context_resolve_color (GtkStyleContext    *context,
+                                  GtkCssValue        *color,
+                                  GdkRGBA            *result,
+                                  GtkCssDependencies *dependencies)
 {
   GtkCssValue *val;
 
@@ -2543,10 +2823,11 @@ _gtk_style_context_resolve_color (GtkStyleContext  *context,
   g_return_val_if_fail (color != NULL, FALSE);
   g_return_val_if_fail (result != NULL, FALSE);
 
-  val = _gtk_symbolic_color_resolve_full (color,
-                                          _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR),
-                                         gtk_style_context_color_lookup_func,
-                                         context);
+  val = _gtk_css_color_value_resolve (color,
+                                      GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade),
+                                      _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR),
+                                      GTK_CSS_DEPENDS_ON_COLOR,
+                                      dependencies);
   if (val == NULL)
     return FALSE;
 
@@ -2570,17 +2851,17 @@ gtk_style_context_lookup_color (GtkStyleContext *context,
                                 const gchar     *color_name,
                                 GdkRGBA         *color)
 {
-  GtkSymbolicColor *sym_color;
+  GtkCssValue *value;
 
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
   g_return_val_if_fail (color_name != NULL, FALSE);
   g_return_val_if_fail (color != NULL, FALSE);
 
-  sym_color = gtk_style_context_color_lookup_func (context, color_name);
-  if (sym_color == NULL)
+  value = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade), color_name);
+  if (value == NULL)
     return FALSE;
 
-  return _gtk_style_context_resolve_color (context, sym_color, color);
+  return _gtk_style_context_resolve_color (context, value, color, NULL);
 }
 
 /**
@@ -2753,20 +3034,49 @@ static void
 gtk_style_context_clear_cache (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GSList *list;
+  GtkStyleInfo *info;
 
   priv = context->priv;
 
-  for (list = priv->info_stack; list; list = list->next)
+  for (info = priv->info; info; info = info->next)
     {
-      GtkStyleInfo *info = list->data;
-      info->data = NULL;
+      style_info_set_data (info, NULL);
     }
   g_hash_table_remove_all (priv->style_data);
 }
 
 static void
-gtk_style_context_do_invalidate (GtkStyleContext *context)
+gtk_style_context_update_cache (GtkStyleContext  *context,
+                                const GtkBitmask *parent_changes)
+{
+  GtkStyleContextPrivate *priv;
+  GHashTableIter iter;
+  gpointer key, value;
+
+  if (_gtk_bitmask_is_empty (parent_changes))
+    return;
+
+  priv = context->priv;
+
+  g_hash_table_iter_init (&iter, priv->style_data);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      GtkStyleInfo *info = key;
+      StyleData *data = value;
+      GtkBitmask *changes;
+
+      changes = _gtk_css_computed_values_compute_dependencies (data->store, parent_changes);
+
+      if (!_gtk_bitmask_is_empty (changes))
+       build_properties (context, data->store, info, changes);
+
+      _gtk_bitmask_free (changes);
+    }
+}
+
+static void
+gtk_style_context_do_invalidate (GtkStyleContext  *context,
+                                 const GtkBitmask *changes)
 {
   GtkStyleContextPrivate *priv;
 
@@ -2778,32 +3088,36 @@ gtk_style_context_do_invalidate (GtkStyleContext *context)
   if (priv->invalidating_context)
     return;
 
-  priv->invalidating_context = TRUE;
+  priv->invalidating_context = changes;
 
   g_signal_emit (context, signals[CHANGED], 0);
 
-  priv->invalidating_context = FALSE;
+  priv->invalidating_context = NULL;
 }
 
-void
-_gtk_style_context_validate (GtkStyleContext *context,
-                             gint64           timestamp,
-                             GtkCssChange     change)
+static GtkBitmask *
+gtk_style_context_update_animations (GtkStyleContext *context,
+                                     gint64           timestamp)
 {
-  GtkStyleContextPrivate *priv;
-  GSList *list;
-
-  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  GtkBitmask *differences;
+  StyleData *style_data;
+  
+  style_data = style_data_lookup (context);
 
-  priv = context->priv;
+  differences = _gtk_css_computed_values_advance (style_data->store,
+                                                  timestamp);
 
-  change |= priv->pending_changes;
+  if (_gtk_css_computed_values_is_static (style_data->store))
+    _gtk_style_context_update_animating (context);
 
-  if (!priv->invalid && change == 0)
-    return;
+  return differences;
+}
 
-  priv->pending_changes = 0;
-  gtk_style_context_set_invalid (context, FALSE);
+static gboolean
+gtk_style_context_needs_full_revalidate (GtkStyleContext  *context,
+                                         GtkCssChange      change)
+{
+  GtkStyleContextPrivate *priv = context->priv;
 
   /* Try to avoid invalidating if we can */
   if (change & GTK_STYLE_CONTEXT_RADICAL_CHANGE)
@@ -2815,13 +3129,18 @@ _gtk_style_context_validate (GtkStyleContext *context,
       if (priv->relevant_changes == GTK_CSS_CHANGE_ANY)
         {
           GtkWidgetPath *path;
-          GtkCssMatcher matcher;
+          GtkCssMatcher matcher, superset;
 
-          path = create_query_path (context);
-          _gtk_css_matcher_init (&matcher, path, ((GtkStyleInfo *) priv->info_stack->data)->state_flags);
+          path = create_query_path (context, priv->info);
+          if (_gtk_css_matcher_init (&matcher, path, priv->info->state_flags))
+            {
+              _gtk_css_matcher_superset_init (&superset, &matcher, GTK_STYLE_CONTEXT_RADICAL_CHANGE & ~GTK_CSS_CHANGE_SOURCE);
+              priv->relevant_changes = _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                                                                               &superset);
+            }
+          else
+            priv->relevant_changes = 0;
 
-          priv->relevant_changes = _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
-                                                                           &matcher);
           priv->relevant_changes &= ~GTK_STYLE_CONTEXT_RADICAL_CHANGE;
 
           gtk_widget_path_unref (path);
@@ -2829,40 +3148,149 @@ _gtk_style_context_validate (GtkStyleContext *context,
     }
 
   if (priv->relevant_changes & change)
-    {
-      GtkStyleInfo *info = priv->info_stack->data;
-      GtkCssComputedValues *old, *new;
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static gboolean
+gtk_style_context_should_create_transitions (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv;
+  gboolean animate;
+
+  priv = context->priv;
+
+  if (priv->widget == NULL)
+    return FALSE;
+
+  if (!gtk_widget_get_mapped (priv->widget))
+    return FALSE;
+
+  g_object_get (gtk_widget_get_settings (context->priv->widget),
+                "gtk-enable-animations", &animate,
+                NULL);
+
+  return animate;
+}
+
+void
+_gtk_style_context_validate (GtkStyleContext  *context,
+                             gint64            timestamp,
+                             GtkCssChange      change,
+                             const GtkBitmask *parent_changes)
+{
+  GtkStyleContextPrivate *priv;
+  GtkStyleInfo *info;
+  StyleData *current;
+  GtkBitmask *changes;
+  GSList *list;
 
-      old = info->data ? g_object_ref (info->data->store) : NULL;
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+
+  change |= priv->pending_changes;
+  
+  /* If you run your application with
+   *   GTK_DEBUG=no-css-cache
+   * every invalidation will purge the cache and completely query
+   * everything anew form the cache. This is slow (in particular
+   * when animating), but useful for figuring out bugs.
+   *
+   * We achieve that by pretending that everything that could have
+   * changed has and so we of course totally need to redo everything.
+   *
+   * Note that this also completely revalidates child widgets all
+   * the time.
+   */
+  if (G_UNLIKELY (gtk_get_debug_flags () & GTK_DEBUG_NO_CSS_CACHE))
+    change = GTK_CSS_CHANGE_ANY;
+
+  if (!priv->invalid && change == 0 && _gtk_bitmask_is_empty (parent_changes))
+    return;
+
+  priv->pending_changes = 0;
+  gtk_style_context_set_invalid (context, FALSE);
+
+  info = priv->info;
+  if (info->data)
+    current = style_data_ref (info->data);
+  else
+    current = NULL;
+
+  /* Try to avoid invalidating if we can */
+  if (current == NULL ||
+      gtk_style_context_needs_full_revalidate (context, change))
+    {
+      StyleData *data;
 
       if ((priv->relevant_changes & change) & ~GTK_STYLE_CONTEXT_CACHED_CHANGE)
-        gtk_style_context_clear_cache (context);
+        {
+          gtk_style_context_clear_cache (context);
+        }
       else
-        info->data = NULL;
-
-      if (old)
         {
-          GtkBitmask *bitmask;
+          gtk_style_context_update_cache (context, parent_changes);
+          style_info_set_data (info, NULL);
+        }
 
-          new = style_data_lookup (context)->store;
+      data = style_data_lookup (context);
 
-          bitmask = _gtk_css_computed_values_get_difference (new, old);
-          if (!_gtk_bitmask_is_empty (bitmask))
-            gtk_style_context_do_invalidate (context);
+      _gtk_css_computed_values_create_animations (data->store,
+                                                  priv->parent ? style_data_lookup (priv->parent)->store : NULL,
+                                                  timestamp,
+                                                  GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                                                  current && gtk_style_context_should_create_transitions (context) ? current->store : NULL);
+      if (_gtk_css_computed_values_is_static (data->store))
+        change &= ~GTK_CSS_CHANGE_ANIMATE;
+      else
+        change |= GTK_CSS_CHANGE_ANIMATE;
+      _gtk_style_context_update_animating (context);
+
+      if (current)
+        {
+          changes = _gtk_css_computed_values_get_difference (data->store, current->store);
 
-          _gtk_bitmask_free (bitmask);
-          g_object_unref (old);
+          /* In the case where we keep the cache, we want unanimated values */
+          _gtk_css_computed_values_cancel_animations (current->store);
         }
       else
-        gtk_style_context_do_invalidate (context);
+        {
+          changes = _gtk_bitmask_new ();
+          changes = _gtk_bitmask_invert_range (changes, 0, _gtk_css_style_property_get_n_properties ());
+        }
+    }
+  else
+    {
+      changes = _gtk_css_computed_values_compute_dependencies (current->store, parent_changes);
+
+      gtk_style_context_update_cache (context, parent_changes);
+    }
+
+  if (current)
+    style_data_unref (current);
 
+  if (change & GTK_CSS_CHANGE_ANIMATE &&
+      gtk_style_context_is_animating (context))
+    {
+      GtkBitmask *animation_changes;
+
+      animation_changes = gtk_style_context_update_animations (context, timestamp);
+      changes = _gtk_bitmask_union (changes, animation_changes);
+      _gtk_bitmask_free (animation_changes);
     }
 
+  if (!_gtk_bitmask_is_empty (changes) || (change & GTK_CSS_CHANGE_FORCE_INVALIDATE))
+    gtk_style_context_do_invalidate (context, changes);
+
   change = _gtk_css_change_for_child (change);
   for (list = priv->children; list; list = list->next)
     {
-      _gtk_style_context_validate (list->data, timestamp, change);
+      _gtk_style_context_validate (list->data, timestamp, change, changes);
     }
+
+  _gtk_bitmask_free (changes);
 }
 
 void
@@ -2876,11 +3304,15 @@ _gtk_style_context_queue_invalidate (GtkStyleContext *context,
 
   priv = context->priv;
 
-  if (priv->widget == NULL && priv->widget_path == NULL)
-    return;
-
-  priv->pending_changes |= change;
-  gtk_style_context_set_invalid (context, TRUE);
+  if (priv->widget != NULL)
+    {
+      priv->pending_changes |= change;
+      gtk_style_context_set_invalid (context, TRUE);
+    }
+  else if (priv->widget_path == NULL)
+    {
+      gtk_style_context_invalidate (context);
+    }
 }
 
 /**
@@ -2899,10 +3331,25 @@ _gtk_style_context_queue_invalidate (GtkStyleContext *context,
 void
 gtk_style_context_invalidate (GtkStyleContext *context)
 {
+  GtkBitmask *changes;
+
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
   gtk_style_context_clear_cache (context);
-  gtk_style_context_do_invalidate (context);
+
+  changes = _gtk_bitmask_new ();
+  changes = _gtk_bitmask_invert_range (changes,
+                                       0,
+                                       _gtk_css_style_property_get_n_properties ());
+  gtk_style_context_do_invalidate (context, changes);
+  _gtk_bitmask_free (changes);
+}
+
+static gboolean
+corner_value_is_right_angle (GtkCssValue *value)
+{
+  return _gtk_css_corner_value_get_x (value, 100) <= 0.0 &&
+         _gtk_css_corner_value_get_y (value, 100) <= 0.0;
 }
 
 /**
@@ -2919,31 +3366,33 @@ void
 gtk_style_context_set_background (GtkStyleContext *context,
                                   GdkWindow       *window)
 {
-  GtkStateFlags state;
-  cairo_pattern_t *pattern;
-  GdkRGBA *color;
+  const GdkRGBA *color;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (GDK_IS_WINDOW (window));
 
-  state = gtk_style_context_get_state (context);
-  gtk_style_context_get (context, state,
-                         "background-image", &pattern,
-                         NULL);
-  if (pattern)
-    {
-      gdk_window_set_background_pattern (window, pattern);
-      cairo_pattern_destroy (pattern);
-      return;
-    }
+  /* This is a sophisitcated optimization.
+   * If we know the GDK window's background will be opaque, we mark
+   * it as opaque. This is so GDK can do all the optimizations it does
+   * for opaque windows and be fast.
+   * This is mainly used when scrolling.
+   *
+   * We could indeed just set black instead of the color we have.
+   */
+  color = _gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR));
 
-  gtk_style_context_get (context, state,
-                         "background-color", &color,
-                         NULL);
-  if (color)
+  if (color->alpha >= 1.0 &&
+      corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS)) &&
+      corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS)) &&
+      corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS)) &&
+      corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS)))
     {
       gdk_window_set_background_rgba (window, color);
-      gdk_rgba_free (color);
+    }
+  else
+    {
+      GdkRGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
+      gdk_window_set_background_rgba (window, &transparent);
     }
 }
 
@@ -3153,6 +3602,9 @@ gtk_style_context_get_margin (GtkStyleContext *context,
  *          freed.
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.8: Use gtk_style_context_get() for "font" or
+ *     subproperties instead.
  **/
 const PangoFontDescription *
 gtk_style_context_get_font (GtkStyleContext *context,
@@ -3160,29 +3612,35 @@ gtk_style_context_get_font (GtkStyleContext *context,
 {
   GtkStyleContextPrivate *priv;
   StyleData *data;
-  PangoFontDescription *description;
+  PangoFontDescription *description, *previous;
 
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
 
   priv = context->priv;
   g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
 
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  data = style_data_lookup (context);
-  gtk_style_context_restore (context);
+  data = style_data_lookup_for_state (context, state);
 
   /* Yuck, fonts are created on-demand but we don't return a ref.
    * Do bad things to achieve this requirement */
-  description = g_object_get_data (G_OBJECT (data->store), "font-cache-for-get_font");
-  if (description == NULL)
+  gtk_style_context_get (context, state, "font", &description, NULL);
+  
+  previous = g_object_get_data (G_OBJECT (data->store), "font-cache-for-get_font");
+
+  if (previous)
+    {
+      pango_font_description_merge (previous, description, TRUE);
+      pango_font_description_free (description);
+      description = previous;
+    }
+  else
     {
-      gtk_style_context_get (context, state, "font", &description, NULL);
       g_object_set_data_full (G_OBJECT (data->store),
                               "font-cache-for-get_font",
                               description,
                               (GDestroyNotify) pango_font_description_free);
     }
+
   return description;
 }
 
@@ -4189,6 +4647,26 @@ gtk_draw_insertion_cursor (GtkWidget          *widget,
                          draw_arrow);
 }
 
+/**
+ * _gtk_style_context_get_changes:
+ * @context: the context to query
+ *
+ * Queries the context for the changes for the currently executing
+ * GtkStyleContext::invalidate signal. If no signal is currently
+ * emitted, this function returns %NULL.
+ *
+ * FIXME 4.0: Make this part of the signal.
+ *
+ * Returns: %NULL or the currently invalidating changes
+ **/
+const GtkBitmask *
+_gtk_style_context_get_changes (GtkStyleContext *context)
+{
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+  return context->priv->invalidating_context;
+}
+
 static AtkAttributeSet *
 add_attribute (AtkAttributeSet  *attributes,
                AtkTextAttribute  attr,
@@ -4243,3 +4721,21 @@ _gtk_style_context_get_attributes (AtkAttributeSet *attributes,
 
   return attributes;
 }
+
+cairo_pattern_t *
+gtk_gradient_resolve_for_context (GtkGradient     *gradient,
+                                  GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+  GtkCssDependencies ignored = 0;
+
+  g_return_val_if_fail (gradient != NULL, NULL);
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+  return _gtk_gradient_resolve_full (gradient,
+                                     GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                                     style_data_lookup (context)->store,
+                                     priv->parent ? style_data_lookup (priv->parent)->store : NULL,
+                                     &ignored);
+}
+