]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkstylecontext.c
Minor documentation improvements
[~andy/gtk] / gtk / gtkstylecontext.c
index 7e1dd2c8b1047e8aea4c1f9ba9a8c3ac88964e57..95ef77cbaf58aefde3ea2600e9afdcf1805830d4 100644 (file)
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <gobject/gvaluecollector.h>
 
-#include "gtkstylecontext.h"
+#include "gtkstylecontextprivate.h"
 #include "gtktypebuiltins.h"
 #include "gtkthemingengine.h"
 #include "gtkintl.h"
@@ -33,6 +33,8 @@
 #include "gtksymboliccolor.h"
 #include "gtkanimationdescription.h"
 #include "gtktimeline.h"
+#include "gtkiconfactory.h"
+#include "gtkwidgetprivate.h"
 
 /**
  * SECTION:gtkstylecontext
  * If you are using custom styling on an applications, you probably want then
  * to make your style information prevail to the theme's, so you must use
  * a #GtkStyleProvider with the %GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
- * priority, keep in mind that the user settings in $HOME/.gtk-3.0.css will
+ * priority, keep in mind that the user settings in
+ * <filename><replaceable>XDG_CONFIG_HOME</replaceable>/gtk-3.0/gtk.css</filename> will
  * still take precedence over your changes, as it uses the
  * %GTK_STYLE_PROVIDER_PRIORITY_USER priority.
  * </para>
  * </refsect2>
  */
 
-typedef struct GtkStyleContextPrivate GtkStyleContextPrivate;
 typedef struct GtkStyleProviderData GtkStyleProviderData;
 typedef struct GtkStyleInfo GtkStyleInfo;
 typedef struct GtkRegion GtkRegion;
@@ -427,6 +429,7 @@ struct PropertyValue
 {
   GType       widget_type;
   GParamSpec *pspec;
+  GtkStateFlags state;
   GValue      value;
 };
 
@@ -450,6 +453,12 @@ struct AnimationInfo
   GtkTimeline *timeline;
 
   gpointer region_id;
+
+  /* Region stack (until region_id) at the time of
+   * rendering, this is used for nested cancellation.
+   */
+  GSList *parent_regions;
+
   GdkWindow *window;
   GtkStateType state;
   gboolean target_value;
@@ -458,7 +467,7 @@ struct AnimationInfo
   GArray *rectangles;
 };
 
-struct GtkStyleContextPrivate
+struct _GtkStyleContextPrivate
 {
   GdkScreen *screen;
 
@@ -492,9 +501,11 @@ enum {
   LAST_SIGNAL
 };
 
-guint signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0 };
 
 static GQuark provider_list_quark = 0;
+static GdkRGBA fallback_color = { 1.0, 0.75, 0.75, 1.0 };
+static GtkBorder fallback_border = { 0 };
 
 static void gtk_style_context_finalize (GObject *object);
 
@@ -704,7 +715,7 @@ gtk_style_context_init (GtkStyleContext *style_context)
                                             (GDestroyNotify) style_data_free);
   priv->theming_engine = g_object_ref ((gpointer) gtk_theming_engine_load (NULL));
 
-  priv->direction = GTK_TEXT_DIR_RTL;
+  priv->direction = GTK_TEXT_DIR_LTR;
 
   priv->screen = gdk_screen_get_default ();
 
@@ -743,6 +754,7 @@ animation_info_free (AnimationInfo *info)
     cairo_region_destroy (info->invalidation_region);
 
   g_array_free (info->rectangles, TRUE);
+  g_slist_free (info->parent_regions);
   g_slice_free (AnimationInfo, info);
 }
 
@@ -839,19 +851,19 @@ animation_info_new (GtkStyleContext         *context,
   info = g_slice_new0 (AnimationInfo);
 
   info->rectangles = g_array_new (FALSE, FALSE, sizeof (cairo_rectangle_int_t));
-  info->timeline = gtk_timeline_new (duration);
+  info->timeline = _gtk_timeline_new (duration);
   info->window = g_object_ref (window);
   info->state = state;
   info->target_value = target_value;
   info->region_id = region_id;
 
-  gtk_timeline_set_progress_type (info->timeline, progress_type);
-  gtk_timeline_set_loop (info->timeline, loop);
+  _gtk_timeline_set_progress_type (info->timeline, progress_type);
+  _gtk_timeline_set_loop (info->timeline, loop);
 
   if (!loop && !target_value)
     {
-      gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
-      gtk_timeline_rewind (info->timeline);
+      _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
+      _gtk_timeline_rewind (info->timeline);
     }
 
   g_signal_connect (info->timeline, "frame",
@@ -859,7 +871,7 @@ animation_info_new (GtkStyleContext         *context,
   g_signal_connect (info->timeline, "finished",
                     G_CALLBACK (timeline_finished_cb), context);
 
-  gtk_timeline_start (info->timeline);
+  _gtk_timeline_start (info->timeline);
 
   return info;
 }
@@ -928,11 +940,9 @@ gtk_style_context_impl_set_property (GObject      *object,
                                      const GValue *value,
                                      GParamSpec   *pspec)
 {
-  GtkStyleContextPrivate *priv;
   GtkStyleContext *style_context;
 
   style_context = GTK_STYLE_CONTEXT (object);
-  priv = style_context->priv;
 
   switch (prop_id)
     {
@@ -956,8 +966,8 @@ gtk_style_context_impl_get_property (GObject    *object,
                                      GValue     *value,
                                      GParamSpec *pspec)
 {
-  GtkStyleContextPrivate *priv;
   GtkStyleContext *style_context;
+  GtkStyleContextPrivate *priv;
 
   style_context = GTK_STYLE_CONTEXT (object);
   priv = style_context->priv;
@@ -977,8 +987,9 @@ gtk_style_context_impl_get_property (GObject    *object,
 }
 
 static GList *
-find_next_candidate (GList *local,
-                     GList *global)
+find_next_candidate (GList    *local,
+                     GList    *global,
+                     gboolean  ascending)
 {
   if (local && global)
     {
@@ -988,9 +999,9 @@ find_next_candidate (GList *local,
       global_data = global->data;
 
       if (local_data->priority < global_data->priority)
-        return local;
+        return (ascending) ? local : global;
       else
-        return global;
+        return (ascending) ? global : local;
     }
   else if (local)
     return local;
@@ -1014,7 +1025,7 @@ build_properties (GtkStyleContext *context,
   if (priv->screen)
     global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
 
-  while ((elem = find_next_candidate (list, global_list)) != NULL)
+  while ((elem = find_next_candidate (list, global_list, TRUE)) != NULL)
     {
       GtkStyleProviderData *data;
       GtkStyleProperties *provider_style;
@@ -1053,7 +1064,7 @@ build_icon_factories (GtkStyleContext *context,
       global_list = g_list_last (global_list);
     }
 
-  while ((elem = find_next_candidate (list, global_list)) != NULL)
+  while ((elem = find_next_candidate (list, global_list, FALSE)) != NULL)
     {
       GtkIconFactory *factory;
       GtkStyleProviderData *data;
@@ -1072,7 +1083,7 @@ build_icon_factories (GtkStyleContext *context,
     }
 }
 
-GtkWidgetPath *
+static GtkWidgetPath *
 create_query_path (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
@@ -1151,6 +1162,10 @@ style_data_lookup (GtkStyleContext *context)
   gtk_style_properties_get (data->store, 0,
                             "engine", &priv->theming_engine,
                             NULL);
+
+  if (!priv->theming_engine)
+    priv->theming_engine = g_object_ref (gtk_theming_engine_load (NULL));
+
   return data;
 }
 
@@ -1264,6 +1279,10 @@ gtk_style_context_new (void)
  *
  * Adds a style provider to @context, to be used in style construction.
  *
+ * <note><para>If both priorities are the same, A #GtkStyleProvider
+ * added through this function takes precedence over another added
+ * through gtk_style_context_add_provider_for_screen().</para></note>
+ *
  * Since: 3.0
  **/
 void
@@ -1329,6 +1348,8 @@ gtk_style_context_reset_widgets (GdkScreen *screen)
 {
   GList *list, *toplevels;
 
+  _gtk_icon_set_invalidate_caches ();
+
   toplevels = gtk_window_list_toplevels ();
   g_list_foreach (toplevels, (GFunc) g_object_ref, NULL);
 
@@ -1360,6 +1381,10 @@ gtk_style_context_reset_widgets (GdkScreen *screen)
  * GTK+ uses this to make styling information from #GtkSettings
  * available.
  *
+ * <note><para>If both priorities are the same, A #GtkStyleProvider
+ * added through gtk_style_context_add_provider() takes precedence
+ * over another added through this function.</para></note>
+ *
  * Since: 3.0
  **/
 void
@@ -1563,7 +1588,6 @@ context_has_animatable_region (GtkStyleContext *context,
                                gpointer         region_id)
 {
   GtkStyleContextPrivate *priv;
-  GSList *r;
 
   /* NULL region_id means everything
    * rendered through the style context
@@ -1572,14 +1596,7 @@ context_has_animatable_region (GtkStyleContext *context,
     return TRUE;
 
   priv = context->priv;
-
-  for (r = priv->animation_regions; r; r = r->next)
-    {
-      if (r->data == region_id)
-        return TRUE;
-    }
-
-  return FALSE;
+  return g_slist_find (priv->animation_regions, region_id) != NULL;
 }
 
 /**
@@ -1622,7 +1639,7 @@ gtk_style_context_state_is_running (GtkStyleContext *context,
           context_has_animatable_region (context, info->region_id))
         {
           if (progress)
-            *progress = gtk_timeline_get_progress (info->timeline);
+            *progress = _gtk_timeline_get_progress (info->timeline);
 
           return TRUE;
         }
@@ -2057,6 +2074,26 @@ gtk_style_context_list_regions (GtkStyleContext *context)
   return classes;
 }
 
+gboolean
+_gtk_style_context_check_region_name (const gchar *str)
+{
+  g_return_val_if_fail (str != NULL, FALSE);
+
+  if (!g_ascii_islower (str[0]))
+    return FALSE;
+
+  while (*str)
+    {
+      if (*str != '-' &&
+          !g_ascii_islower (*str))
+        return FALSE;
+
+      str++;
+    }
+
+  return TRUE;
+}
+
 /**
  * gtk_style_context_add_region:
  * @context: a #GtkStyleContext
@@ -2083,6 +2120,9 @@ gtk_style_context_list_regions (GtkStyleContext *context)
  *
  * would apply to even and odd rows, respectively.
  *
+ * <note><para>Region names must only contain lowercase letters
+ * and '-', starting always with a lowercase letter.</para></note>
+ *
  * Since: 3.0
  **/
 void
@@ -2097,6 +2137,7 @@ gtk_style_context_add_region (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (region_name != NULL);
+  g_return_if_fail (_gtk_style_context_check_region_name (region_name));
 
   priv = context->priv;
   region_quark = g_quark_from_string (region_name);
@@ -2220,15 +2261,22 @@ style_property_values_cmp (gconstpointer bsearch_node1,
   const PropertyValue *val1 = bsearch_node1;
   const PropertyValue *val2 = bsearch_node2;
 
-  if (val1->widget_type == val2->widget_type)
-    return val1->pspec < val2->pspec ? -1 : val1->pspec == val2->pspec ? 0 : 1;
-  else
+  if (val1->widget_type != val2->widget_type)
     return val1->widget_type < val2->widget_type ? -1 : 1;
+
+  if (val1->pspec != val2->pspec)
+    return val1->pspec < val2->pspec ? -1 : 1;
+
+  if (val1->state != val2->state)
+    return val1->state < val2->state ? -1 : 1;
+
+  return 0;
 }
 
 const GValue *
 _gtk_style_context_peek_style_property (GtkStyleContext *context,
                                         GType            widget_type,
+                                        GtkStateFlags    state,
                                         GParamSpec      *pspec)
 {
   GtkStyleContextPrivate *priv;
@@ -2241,6 +2289,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
   data = style_data_lookup (context);
 
   key.widget_type = widget_type;
+  key.state = state;
   key.pspec = pspec;
 
   /* need value cache array */
@@ -2280,7 +2329,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
       list = priv->providers_last;
       global = global_list;
 
-      while ((elem = find_next_candidate (list, global)) != NULL)
+      while ((elem = find_next_candidate (list, global, FALSE)) != NULL)
         {
           GtkStyleProviderData *provider_data;
 
@@ -2292,8 +2341,8 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
             global = global->prev;
 
           if (gtk_style_provider_get_style_property (provider_data->provider,
-                                                     priv->widget_path, pspec,
-                                                     &pcache->value))
+                                                     priv->widget_path, state,
+                                                     pspec, &pcache->value))
             {
               /* Resolve symbolic colors to GdkColor/GdkRGBA */
               if (G_VALUE_TYPE (&pcache->value) == GTK_TYPE_SYMBOLIC_COLOR)
@@ -2301,17 +2350,19 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
                   GtkSymbolicColor *color;
                   GdkRGBA rgba;
 
-                  color = g_value_get_boxed (&pcache->value);
+                  color = g_value_dup_boxed (&pcache->value);
+
+                  g_value_unset (&pcache->value);
+
+                  if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
+                    g_value_init (&pcache->value, GDK_TYPE_RGBA);
+                  else
+                    g_value_init (&pcache->value, GDK_TYPE_COLOR);
 
                   if (gtk_symbolic_color_resolve (color, data->store, &rgba))
                     {
-                      g_value_unset (&pcache->value);
-
                       if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
-                        {
-                          g_value_init (&pcache->value, GDK_TYPE_RGBA);
-                          g_value_set_boxed (&pcache->value, &rgba);
-                        }
+                        g_value_set_boxed (&pcache->value, &rgba);
                       else
                         {
                           GdkColor rgb;
@@ -2320,12 +2371,13 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
                           rgb.green = rgba.green * 65535. + 0.5;
                           rgb.blue = rgba.blue * 65535. + 0.5;
 
-                          g_value_init (&pcache->value, GDK_TYPE_COLOR);
                           g_value_set_boxed (&pcache->value, &rgb);
                         }
                     }
                   else
                     g_param_value_set_default (pspec, &pcache->value);
+
+                  gtk_symbolic_color_unref (color);
                 }
 
               return &pcache->value;
@@ -2343,7 +2395,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
  * gtk_style_context_get_style_property:
  * @context: a #GtkStyleContext
  * @property_name: the name of the widget style property
- * @value: (out) (transfer full): Return location for the property value
+ * @value: Return location for the property value
  *
  * Gets the value for a widget style property.
  *
@@ -2357,6 +2409,7 @@ gtk_style_context_get_style_property (GtkStyleContext *context,
 {
   GtkStyleContextPrivate *priv;
   GtkWidgetClass *widget_class;
+  GtkStateFlags state;
   GParamSpec *pspec;
   const GValue *peek_value;
   GType widget_type;
@@ -2370,7 +2423,15 @@ gtk_style_context_get_style_property (GtkStyleContext *context,
   if (!priv->widget_path)
     return;
 
-  widget_type = gtk_widget_path_get_widget_type (priv->widget_path);
+  widget_type = gtk_widget_path_get_object_type (priv->widget_path);
+
+  if (!g_type_is_a (widget_type, GTK_TYPE_WIDGET))
+    {
+      g_warning ("%s: can't get style properties for non-widget class `%s'",
+                 G_STRLOC,
+                 g_type_name (widget_type));
+      return;
+    }
 
   widget_class = g_type_class_ref (widget_type);
   pspec = gtk_widget_class_find_style_property (widget_class, property_name);
@@ -2385,9 +2446,9 @@ gtk_style_context_get_style_property (GtkStyleContext *context,
       return;
     }
 
-  peek_value = _gtk_style_context_peek_style_property (context,
-                                                       widget_type,
-                                                       pspec);
+  state = gtk_style_context_get_state (context);
+  peek_value = _gtk_style_context_peek_style_property (context, widget_type,
+                                                       state, pspec);
 
   if (G_VALUE_TYPE (value) == G_VALUE_TYPE (peek_value))
     g_value_copy (peek_value, value);
@@ -2416,6 +2477,8 @@ gtk_style_context_get_style_valist (GtkStyleContext *context,
 {
   GtkStyleContextPrivate *priv;
   const gchar *prop_name;
+  GtkStateFlags state;
+  GType widget_type;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
@@ -2425,16 +2488,25 @@ gtk_style_context_get_style_valist (GtkStyleContext *context,
   if (!priv->widget_path)
     return;
 
+  widget_type = gtk_widget_path_get_object_type (priv->widget_path);
+
+  if (!g_type_is_a (widget_type, GTK_TYPE_WIDGET))
+    {
+      g_warning ("%s: can't get style properties for non-widget class `%s'",
+                 G_STRLOC,
+                 g_type_name (widget_type));
+      return;
+    }
+
+  state = gtk_style_context_get_state (context);
+
   while (prop_name)
     {
       GtkWidgetClass *widget_class;
       GParamSpec *pspec;
       const GValue *peek_value;
-      GType widget_type;
       gchar *error;
 
-      widget_type = gtk_widget_path_get_widget_type (priv->widget_path);
-
       widget_class = g_type_class_ref (widget_type);
       pspec = gtk_widget_class_find_style_property (widget_class, prop_name);
       g_type_class_unref (widget_class);
@@ -2448,9 +2520,8 @@ gtk_style_context_get_style_valist (GtkStyleContext *context,
           continue;
         }
 
-      peek_value = _gtk_style_context_peek_style_property (context,
-                                                           widget_type,
-                                                           pspec);
+      peek_value = _gtk_style_context_peek_style_property (context, widget_type,
+                                                           state, pspec);
 
       G_VALUE_LCOPY (peek_value, args, 0, &error);
 
@@ -2575,7 +2646,7 @@ gtk_style_context_set_screen (GtkStyleContext *context,
  *
  * Returns the #GdkScreen to which @context is attached.
  *
- * Returns: a #GdkScreen, or %NULL.
+ * Returns: (transfer none): a #GdkScreen.
  **/
 GdkScreen *
 gtk_style_context_get_screen (GtkStyleContext *context)
@@ -2838,9 +2909,9 @@ gtk_style_context_notify_state_change (GtkStyleContext *context,
   if (!desc)
     return;
 
-  if (gtk_animation_description_get_duration (desc) == 0)
+  if (_gtk_animation_description_get_duration (desc) == 0)
     {
-      gtk_animation_description_unref (desc);
+      _gtk_animation_description_unref (desc);
       return;
     }
 
@@ -2850,37 +2921,153 @@ gtk_style_context_notify_state_change (GtkStyleContext *context,
       info->target_value != state_value)
     {
       /* Target values are the opposite */
-      if (!gtk_timeline_get_loop (info->timeline))
+      if (!_gtk_timeline_get_loop (info->timeline))
         {
           /* Reverse the animation */
-          if (gtk_timeline_get_direction (info->timeline) == GTK_TIMELINE_DIRECTION_FORWARD)
-            gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
+          if (_gtk_timeline_get_direction (info->timeline) == GTK_TIMELINE_DIRECTION_FORWARD)
+            _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
           else
-            gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_FORWARD);
+            _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_FORWARD);
 
           info->target_value = state_value;
         }
       else
         {
           /* Take it out of its looping state */
-          gtk_timeline_set_loop (info->timeline, FALSE);
+          _gtk_timeline_set_loop (info->timeline, FALSE);
         }
     }
   else if (!info &&
-           (!gtk_animation_description_get_loop (desc) ||
+           (!_gtk_animation_description_get_loop (desc) ||
             state_value))
     {
       info = animation_info_new (context, region_id,
-                                 gtk_animation_description_get_duration (desc),
-                                 gtk_animation_description_get_progress_type (desc),
-                                 gtk_animation_description_get_loop (desc),
+                                 _gtk_animation_description_get_duration (desc),
+                                 _gtk_animation_description_get_progress_type (desc),
+                                 _gtk_animation_description_get_loop (desc),
                                  state, state_value, window);
 
       priv->animations = g_slist_prepend (priv->animations, info);
       priv->animations_invalidated = TRUE;
     }
 
-  gtk_animation_description_unref (desc);
+  _gtk_animation_description_unref (desc);
+}
+
+/**
+ * gtk_style_context_cancel_animations:
+ * @context: a #GtkStyleContext
+ * @region_id: (allow-none): animatable region to stop, or %NULL.
+ *     See gtk_style_context_push_animatable_region()
+ *
+ * Stops all running animations for @region_id and all animatable
+ * regions underneath.
+ *
+ * A %NULL @region_id will stop all ongoing animations in @context,
+ * when dealing with a #GtkStyleContext obtained through
+ * gtk_widget_get_style_context(), this is normally done for you
+ * in all circumstances you would expect all widget to be stopped,
+ * so this should be only used in complex widgets with different
+ * animatable regions.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_cancel_animations (GtkStyleContext *context,
+                                     gpointer         region_id)
+{
+  GtkStyleContextPrivate *priv;
+  AnimationInfo *info;
+  GSList *l;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  l = priv->animations;
+
+  while (l)
+    {
+      info = l->data;
+      l = l->next;
+
+      if (!region_id ||
+          info->region_id == region_id ||
+          g_slist_find (info->parent_regions, region_id))
+        {
+          priv->animations = g_slist_remove (priv->animations, info);
+          animation_info_free (info);
+        }
+    }
+}
+
+static gboolean
+is_parent_of (GdkWindow *parent,
+              GdkWindow *child)
+{
+  GtkWidget *child_widget, *parent_widget;
+  GdkWindow *window;
+
+  gdk_window_get_user_data (child, (gpointer *) &child_widget);
+  gdk_window_get_user_data (parent, (gpointer *) &parent_widget);
+
+  if (child_widget != parent_widget &&
+      !gtk_widget_is_ancestor (child_widget, parent_widget))
+    return FALSE;
+
+  window = child;
+
+  while (window)
+    {
+      if (window == parent)
+        return TRUE;
+
+      window = gdk_window_get_parent (window);
+    }
+
+  return FALSE;
+}
+
+/**
+ * gtk_style_context_scroll_animations:
+ * @context: a #GtkStyleContext
+ * @window: a #GdkWindow used previously in
+ *          gtk_style_context_notify_state_change()
+ * @dx: Amount to scroll in the X axis
+ * @dy: Amount to scroll in the Y axis
+ *
+ * This function is analogous to gdk_window_scroll(), and
+ * should be called together with it so the invalidation
+ * areas for any ongoing animation are scrolled together
+ * with it.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_scroll_animations (GtkStyleContext *context,
+                                     GdkWindow       *window,
+                                     gint             dx,
+                                     gint             dy)
+{
+  GtkStyleContextPrivate *priv;
+  AnimationInfo *info;
+  GSList *l;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  priv = context->priv;
+  l = priv->animations;
+
+  while (l)
+    {
+      info = l->data;
+      l = l->next;
+
+      if (info->invalidation_region &&
+          (window == info->window ||
+           is_parent_of (window, info->window)))
+        cairo_region_translate (info->invalidation_region, dx, dy);
+    }
 }
 
 /**
@@ -2965,8 +3152,7 @@ _gtk_style_context_invalidate_animation_areas (GtkStyleContext *context)
 
 void
 _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
-                                             gint             rel_x,
-                                             gint             rel_y)
+                                            GtkWidget       *widget)
 {
   GtkStyleContextPrivate *priv;
   GSList *l;
@@ -2981,6 +3167,7 @@ _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
   while (l)
     {
       AnimationInfo *info;
+      gint rel_x, rel_y;
       GSList *cur;
       guint i;
 
@@ -2991,23 +3178,23 @@ _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
       if (info->invalidation_region)
         continue;
 
-      /* There's not much point in keeping the animation running */
       if (info->rectangles->len == 0)
-        {
-          priv->animations = g_slist_remove (priv->animations, info);
-          animation_info_free (info);
-          continue;
-        }
+        continue;
 
       info->invalidation_region = cairo_region_create ();
+      _gtk_widget_get_translation_to_window (widget, info->window, &rel_x, &rel_y);
 
       for (i = 0; i < info->rectangles->len; i++)
         {
           cairo_rectangle_int_t *rect;
 
           rect = &g_array_index (info->rectangles, cairo_rectangle_int_t, i);
-          rect->x += rel_x;
-          rect->y += rel_y;
+
+         /* These are widget relative coordinates,
+          * so have them inverted to be window relative
+          */
+          rect->x -= rel_x;
+          rect->y -= rel_y;
 
           cairo_region_union_rectangle (info->invalidation_region, rect);
         }
@@ -3055,6 +3242,14 @@ store_animation_region (GtkStyleContext *context,
           rect.height = (gint) height;
 
           g_array_append_val (info->rectangles, rect);
+
+          if (!info->parent_regions)
+            {
+              GSList *parent_regions;
+
+              parent_regions = g_slist_find (priv->animation_regions, info->region_id);
+              info->parent_regions = g_slist_copy (parent_regions);
+            }
         }
     }
 }
@@ -3137,6 +3332,330 @@ gtk_style_context_set_background (GtkStyleContext *context,
     }
 }
 
+/**
+ * gtk_style_context_get_color:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the foreground color
+ *
+ * Gets the foreground color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_color (GtkStyleContext *context,
+                             GtkStateFlags    state,
+                             GdkRGBA         *color)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+  GdkRGBA *c;
+
+  g_return_if_fail (color != NULL);
+  *color = fallback_color;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  g_return_if_fail (priv->widget_path != NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store,
+                                               "color", state);
+
+  if (value)
+    {
+      c = g_value_get_boxed (value);
+      *color = *c;
+    }
+}
+
+/**
+ * gtk_style_context_get_background_color:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the background color
+ *
+ * Gets the background color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_background_color (GtkStyleContext *context,
+                                        GtkStateFlags    state,
+                                        GdkRGBA         *color)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+  GdkRGBA *c;
+
+  g_return_if_fail (color != NULL);
+  *color = fallback_color;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  g_return_if_fail (priv->widget_path != NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store,
+                                               "background-color", state);
+
+  if (value)
+    {
+      c = g_value_get_boxed (value);
+      *color = *c;
+    }
+}
+
+/**
+ * gtk_style_context_get_border_color:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the border color
+ *
+ * Gets the border color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_border_color (GtkStyleContext *context,
+                                    GtkStateFlags    state,
+                                    GdkRGBA         *color)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+  GdkRGBA *c;
+
+  g_return_if_fail (color != NULL);
+  *color = fallback_color;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  g_return_if_fail (priv->widget_path != NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store,
+                                               "border-color", state);
+
+  if (value)
+    {
+      c = g_value_get_boxed (value);
+      *color = *c;
+    }
+}
+
+/**
+ * gtk_style_context_get_border:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the border for
+ * @border: (out): return value for the border settings
+ *
+ * Gets the border for a given state as a #GtkBorder.
+ * See %GTK_STYLE_PROPERTY_BORDER_WIDTH.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_border (GtkStyleContext *context,
+                              GtkStateFlags    state,
+                              GtkBorder       *border)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+  GtkBorder *b;
+
+  g_return_if_fail (border != NULL);
+  *border = fallback_border;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  g_return_if_fail (priv->widget_path != NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store,
+                                               "border-width", state);
+
+  if (value)
+    {
+      b = g_value_get_boxed (value);
+      *border = *b;
+    }
+}
+
+/**
+ * gtk_style_context_get_padding:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the padding for
+ * @padding: (out): return value for the padding settings
+ *
+ * Gets the padding for a given state as a #GtkBorder.
+ * See %GTK_STYLE_PROPERTY_PADDING.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_padding (GtkStyleContext *context,
+                               GtkStateFlags    state,
+                               GtkBorder       *padding)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+  GtkBorder *b;
+
+  g_return_if_fail (padding != NULL);
+  *padding = fallback_border;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  g_return_if_fail (priv->widget_path != NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store,
+                                               "padding", state);
+
+  if (value)
+    {
+      b = g_value_get_boxed (value);
+      *padding = *b;
+    }
+}
+
+/**
+ * gtk_style_context_get_margin:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the border for
+ * @margin: (out): return value for the margin settings
+ *
+ * Gets the margin for a given state as a #GtkBorder.
+ * See %GTK_STYLE_PROPERTY_MARGIN.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_margin (GtkStyleContext *context,
+                              GtkStateFlags    state,
+                              GtkBorder       *margin)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+  GtkBorder *b;
+
+  g_return_if_fail (margin != NULL);
+  *margin = fallback_border;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+  priv = context->priv;
+  g_return_if_fail (priv->widget_path != NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store,
+                                               "margin", state);
+
+  if (value)
+    {
+      b = g_value_get_boxed (value);
+      *margin = *b;
+    }
+}
+
+/**
+ * gtk_style_context_get_font:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the font for
+ *
+ * Returns the font description for a given state. The returned
+ * object is const and will remain valid until the
+ * #GtkStyleContext::changed signal happens.
+ *
+ * Returns: (transfer none): the #PangoFontDescription for the given
+ *          state.  This object is owned by GTK+ and should not be
+ *          freed.
+ *
+ * Since: 3.0
+ **/
+const PangoFontDescription *
+gtk_style_context_get_font (GtkStyleContext *context,
+                            GtkStateFlags    state)
+{
+  GtkStyleContextPrivate *priv;
+  StyleData *data;
+  const GValue *value;
+
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+  priv = context->priv;
+  g_return_val_if_fail (priv->widget_path != NULL, NULL);
+
+  data = style_data_lookup (context);
+  value = _gtk_style_properties_peek_property (data->store, "font", state);
+
+  if (value)
+    return g_value_get_boxed (value);
+
+  return NULL;
+}
+
+static void
+get_cursor_color (GtkStyleContext *context,
+                  gboolean         primary,
+                  GdkRGBA         *color)
+{
+  GdkColor *style_color;
+
+  gtk_style_context_get_style (context,
+                               primary ? "cursor-color" : "secondary-cursor-color",
+                               &style_color,
+                               NULL);
+
+  if (style_color)
+    {
+      color->red = style_color->red / 65535;
+      color->green = style_color->green / 65535;
+      color->blue = style_color->blue / 65535;
+      color->alpha = 1;
+
+      gdk_color_free (style_color);
+    }
+  else
+    {
+      gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, color);
+
+      if (!primary)
+      {
+        GdkRGBA bg;
+
+        gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &bg);
+
+        color->red = (color->red + bg.red) * 0.5;
+        color->green = (color->green + bg.green) * 0.5;
+        color->blue = (color->blue + bg.blue) * 0.5;
+      }
+    }
+}
+
+void
+_gtk_style_context_get_cursor_color (GtkStyleContext *context,
+                                     GdkRGBA         *primary_color,
+                                     GdkRGBA         *secondary_color)
+{
+  if (primary_color)
+    get_cursor_color (context, TRUE, primary_color);
+
+  if (secondary_color)
+    get_cursor_color (context, FALSE, secondary_color);
+}
+
 /* Paint methods */
 
 /**
@@ -3174,15 +3693,21 @@ gtk_render_check (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_check (priv->theming_engine, cr,
                               x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3218,15 +3743,21 @@ gtk_render_option (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_option (priv->theming_engine, cr,
                                x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3260,15 +3791,20 @@ gtk_render_arrow (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (size > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, size, size);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_arrow (priv->theming_engine, cr,
                               angle, x, y, size);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3305,14 +3841,20 @@ gtk_render_background (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_background (priv->theming_engine, cr, x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3351,14 +3893,20 @@ gtk_render_frame (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_frame (priv->theming_engine, cr, x, y, width, height);
+  
+  cairo_restore (cr);
 }
 
 /**
@@ -3394,14 +3942,20 @@ gtk_render_expander (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_expander (priv->theming_engine, cr, x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3434,14 +3988,20 @@ gtk_render_focus (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_focus (priv->theming_engine, cr, x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3468,11 +4028,14 @@ gtk_render_layout (GtkStyleContext *context,
   PangoRectangle extents;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (PANGO_IS_LAYOUT (layout));
   g_return_if_fail (cr != NULL);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   pango_layout_get_extents (layout, &extents, NULL);
 
   store_animation_region (context,
@@ -3483,6 +4046,8 @@ gtk_render_layout (GtkStyleContext *context,
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_layout (priv->theming_engine, cr, x, y, layout);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3515,8 +4080,12 @@ gtk_render_line (GtkStyleContext *context,
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_line (priv->theming_engine, cr, x0, y0, x1, y1);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3554,14 +4123,20 @@ gtk_render_slider (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_slider (priv->theming_engine, cr, x, y, width, height, orientation);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3604,16 +4179,30 @@ gtk_render_frame_gap (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
+  g_return_if_fail (xy0_gap <= xy1_gap);
+  g_return_if_fail (xy0_gap >= 0);
+
+  if (gap_side == GTK_POS_LEFT ||
+      gap_side == GTK_POS_RIGHT)
+    g_return_if_fail (xy1_gap <= height);
+  else
+    g_return_if_fail (xy1_gap <= width);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_frame_gap (priv->theming_engine, cr,
                                   x, y, width, height, gap_side,
                                   xy0_gap, xy1_gap);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3651,14 +4240,20 @@ gtk_render_extension (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_extension (priv->theming_engine, cr, x, y, width, height, gap_side);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3694,14 +4289,20 @@ gtk_render_handle (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_handle (priv->theming_engine, cr, x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3732,14 +4333,20 @@ gtk_render_activity (GtkStyleContext *context,
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
   store_animation_region (context, x, y, width, height);
 
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   engine_class->render_activity (priv->theming_engine, cr, x, y, width, height);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -3765,7 +4372,7 @@ gtk_render_icon_pixbuf (GtkStyleContext     *context,
   GtkThemingEngineClass *engine_class;
 
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
-  g_return_val_if_fail (size == -1 || size <= GTK_ICON_SIZE_DIALOG, NULL);
+  g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
   g_return_val_if_fail (source != NULL, NULL);
 
   priv = context->priv;