]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkstylecontext.c
wayland: Report middle and right buttons correctly
[~andy/gtk] / gtk / gtkstylecontext.c
index e42ebf8aace7ccfc834afc590674b57fc7f67b11..370c782055d22364375168a524cfbf03a82f4e42 100644 (file)
 #include "config.h"
 
 #include <gdk/gdk.h>
+#include <math.h>
 #include <stdlib.h>
 #include <gobject/gvaluecollector.h>
 
-#include "gtkstylecontext.h"
+#include "gtkstylecontextprivate.h"
+#include "gtkstylepropertiesprivate.h"
 #include "gtktypebuiltins.h"
 #include "gtkthemingengine.h"
 #include "gtkintl.h"
 #include "gtkwidget.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
-#include "gtksymboliccolor.h"
+#include "gtksymboliccolorprivate.h"
 #include "gtkanimationdescription.h"
 #include "gtktimeline.h"
 #include "gtkiconfactory.h"
 #include "gtkwidgetprivate.h"
+#include "gtkstyleproviderprivate.h"
 
 /**
  * SECTION:gtkstylecontext
  * can be either attached explicitly to the context through
  * gtk_style_context_add_provider(), or to the screen through
  * gtk_style_context_add_provider_for_screen(). The resulting style is a
- * combination of all provider's information in priority order.
+ * combination of all providers' information in priority order.
  *
  * For GTK+ widgets, any #GtkStyleContext returned by
  * gtk_widget_get_style_context() will already have a #GtkWidgetPath, a
- * #GdkScreen and RTL/LTR information set, the style context will be also
+ * #GdkScreen and RTL/LTR information set. The style context will be also
  * updated automatically if any of these settings change on the widget.
  *
- * If you are using are the theming layer standalone, you will need to set a
+ * If you are using the theming layer standalone, you will need to set a
  * widget path and a screen yourself to the created style context through
  * gtk_style_context_set_path() and gtk_style_context_set_screen(), as well
  * as updating the context yourself using gtk_style_context_invalidate()
  * Widgets can add style classes to their context, which can be used
  * to associate different styles by class (see <xref linkend="gtkcssprovider-selectors"/>). Theme engines can also use style classes to vary their
  * rendering. GTK+ has a number of predefined style classes:
- * <informaltable>
- *   <tgroup cols="3">
- *     <thead>
- *       <row>
- *         <entry>Style class</entry>
- *         <entry>Macro</entry>
- *         <entry>Used by</entry>
- *       </row>
- *     </thead>
- *     <tbody>
- *       <row>
- *         <entry>button</entry>
- *         <entry>GTK_STYLE_CLASS_BUTTON</entry>
- *         <entry>#GtkButton, #GtkToggleButton, #GtkRadioButton, #GtkCheckButton</entry>
- *       </row>
- *       <row>
- *         <entry>default</entry>
- *         <entry>GTK_STYLE_CLASS_DEFAULT</entry>
- *         <entry>#GtkButton</entry>
- *       </row>
- *       <row>
- *         <entry>check</entry>
- *         <entry>GTK_STYLE_CLASS_CHECK</entry>
- *         <entry>#GtkCheckButton, #GtkCheckMenuItem, #GtkCellRendererToggle</entry>
- *       </row>
- *       <row>
- *         <entry>radio</entry>
- *         <entry>GTK_STYLE_CLASS_RADIO</entry>
- *         <entry>#GtkRadioButton, #GtkRadioMenuItem, #GtkCellRendererToggle</entry>
- *       </row>
- *       <row>
- *         <entry>arrow</entry>
- *         <entry>GTK_STYLE_CLASS_ARROW</entry>
- *         <entry>#GtkArrow</entry>
- *       </row>
- *       <row>
- *         <entry>calendar</entry>
- *         <entry>GTK_STYLE_CLASS_CALENDAR</entry>
- *         <entry>#GtkCalendar</entry>
- *       </row>
- *       <row>
- *         <entry>entry</entry>
- *         <entry>GTK_STYLE_CLASS_ENTRY</entry>
- *         <entry>#GtkEntry</entry>
- *       </row>
- *       <row>
- *         <entry>cell</entry>
- *         <entry>GTK_STYLE_CLASS_CELL</entry>
- *         <entry>#GtkCellRendererToggle</entry>
- *       </row>
- *       <row>
- *         <entry>menu</entry>
- *         <entry>GTK_STYLE_CLASS_MENU</entry>
- *         <entry>#GtkMenu, #GtkMenuItem, #GtkCheckMenuItem, #GtkRadioMenuItem</entry>
- *       </row>
- *       <row>
- *         <entry>expander</entry>
- *         <entry>GTK_STYLE_CLASS_EXPANDER</entry>
- *         <entry>#GtkExpander</entry>
- *       </row>
- *       <row>
- *         <entry>tooltip</entry>
- *         <entry>GTK_STYLE_CLASS_TOOLTIP</entry>
- *         <entry>#GtkTooltip</entry>
- *       </row>
- *       <row>
- *         <entry>frame</entry>
- *         <entry>GTK_STYLE_CLASS_FRAME</entry>
- *         <entry>#GtkFrame</entry>
- *       </row>
- *       <row>
- *         <entry>scrolled-window</entry>
- *         <entry></entry>
- *         <entry>#GtkScrolledWindow</entry>
- *       </row>
- *       <row>
- *         <entry>viewport</entry>
- *         <entry></entry>
- *         <entry>#GtkViewport</entry>
- *       </row>
- *       <row>
- *         <entry>trough</entry>
- *         <entry>GTK_STYLE_CLASS_TROUGH</entry>
- *         <entry>#GtkScrollbar, #GtkProgressBar, #GtkScale</entry>
- *       </row>
- *       <row>
- *         <entry>progressbar</entry>
- *         <entry>GTK_STYLE_CLASS_PROGRESSBAR</entry>
- *         <entry>#GtkProgressBar, #GtkCellRendererProgress</entry>
- *       </row>
- *       <row>
- *         <entry>slider</entry>
- *         <entry>GTK_STYLE_CLASS_SLIDER</entry>
- *         <entry>#GtkScrollbar, #GtkScale</entry>
- *       </row>
- *       <row>
- *         <entry>menuitem</entry>
- *         <entry>GTK_STYLE_CLASS_MENUITEM</entry>
- *         <entry>#GtkMenuItem</entry>
- *       </row>
- *       <row>
- *         <entry>popup</entry>
- *         <entry></entry>
- *         <entry>#GtkMenu</entry>
- *       </row>
- *       <row>
- *         <entry>accelerator</entry>
- *         <entry>GTK_STYLE_CLASS_ACCELERATOR</entry>
- *         <entry>#GtkAccelLabel</entry>
- *       </row>
- *       <row>
- *         <entry>menubar</entry>
- *         <entry>GTK_STYLE_CLASS_MENUBAR</entry>
- *         <entry>#GtkMenuBar</entry>
- *       </row>
- *       <row>
- *         <entry>toolbar</entry>
- *         <entry>GTK_STYLE_CLASS_TOOLBAR</entry>
- *         <entry>#GtkToolbar</entry>
- *       </row>
- *       <row>
- *         <entry>dock</entry>
- *         <entry>GTK_STYLE_CLASS_DOCK</entry>
- *         <entry>#GtkHandleBox</entry>
- *       </row>
- *       <row>
- *         <entry>notebook</entry>
- *         <entry></entry>
- *         <entry>#GtkNotebook</entry>
- *       </row>
- *       <row>
- *         <entry>background</entry>
- *         <entry>GTK_STYLE_CLASS_BACKGROUND</entry>
- *         <entry>#GtkWindow</entry>
- *       </row>
- *       <row>
- *         <entry>rubberband</entry>
- *         <entry>GTK_STYLE_CLASS_RUBBERBAND</entry>
- *         <entry></entry>
- *       </row>
- *       <row>
- *         <entry>header</entry>
- *         <entry>GTK_STYLE_CLASS_HEADER</entry>
- *         <entry></entry>
- *       </row>
- *       <row>
- *         <entry>grip</entry>
- *         <entry>GTK_STYLE_CLASS_GRIP</entry>
- *         <entry>#GtkWindow</entry>
- *       </row>
- *       <row>
- *         <entry>spinner</entry>
- *         <entry>GTK_STYLE_CLASS_SPINNER</entry>
- *         <entry>#GtkSpinner</entry>
- *       </row>
- *     </tbody>
- *   </tgroup>
- * </informaltable>
+ * #GTK_STYLE_CLASS_CELL,
+ * #GTK_STYLE_CLASS_ENTRY,
+ * #GTK_STYLE_CLASS_BUTTON,
+ * #GTK_STYLE_CLASS_COMBOBOX_ENTRY,
+ * #GTK_STYLE_CLASS_CALENDAR,
+ * #GTK_STYLE_CLASS_SLIDER,
+ * #GTK_STYLE_CLASS_BACKGROUND,
+ * #GTK_STYLE_CLASS_RUBBERBAND,
+ * #GTK_STYLE_CLASS_TOOLTIP,
+ * #GTK_STYLE_CLASS_MENU,
+ * #GTK_STYLE_CLASS_MENUBAR,
+ * #GTK_STYLE_CLASS_MENUITEM,
+ * #GTK_STYLE_CLASS_TOOLBAR,
+ * #GTK_STYLE_CLASS_PRIMARY_TOOLBAR,
+ * #GTK_STYLE_CLASS_INLINE_TOOLBAR,
+ * #GTK_STYLE_CLASS_RADIO,
+ * #GTK_STYLE_CLASS_CHECK,
+ * #GTK_STYLE_CLASS_TROUGH,
+ * #GTK_STYLE_CLASS_SCROLLBAR,
+ * #GTK_STYLE_CLASS_SCALE,
+ * #GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE,
+ * #GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW,
+ * #GTK_STYLE_CLASS_HEADER,
+ * #GTK_STYLE_CLASS_ACCELERATOR,
+ * #GTK_STYLE_CLASS_GRIP,
+ * #GTK_STYLE_CLASS_DOCK,
+ * #GTK_STYLE_CLASS_PROGRESSBAR,
+ * #GTK_STYLE_CLASS_SPINNER,
+ * #GTK_STYLE_CLASS_EXPANDER,
+ * #GTK_STYLE_CLASS_SPINBUTTON,
+ * #GTK_STYLE_CLASS_NOTEBOOK,
+ * #GTK_STYLE_CLASS_VIEW,
+ * #GTK_STYLE_CLASS_SIDEBAR,
+ * #GTK_STYLE_CLASS_IMAGE,
+ * #GTK_STYLE_CLASS_HIGHLIGHT,
+ * #GTK_STYLE_CLASS_FRAME,
+ * #GTK_STYLE_CLASS_DND,
+ * #GTK_STYLE_CLASS_PANE_SEPARATOR,
+ * #GTK_STYLE_CLASS_SEPARATOR,
+ * #GTK_STYLE_CLASS_INFO,
+ * #GTK_STYLE_CLASS_WARNING,
+ * #GTK_STYLE_CLASS_QUESTION,
+ * #GTK_STYLE_CLASS_ERROR,
+ * #GTK_STYLE_CLASS_HORIZONTAL,
+ * #GTK_STYLE_CLASS_VERTICAL,
+ * #GTK_STYLE_CLASS_TOP,
+ * #GTK_STYLE_CLASS_BOTTOM,
+ * #GTK_STYLE_CLASS_LEFT,
+ * #GTK_STYLE_CLASS_RIGHT,
  * </para>
  * <para>
  * Widgets can also add regions with flags to their context.
  * </refsect2>
  */
 
-typedef struct GtkStyleContextPrivate GtkStyleContextPrivate;
 typedef struct GtkStyleProviderData GtkStyleProviderData;
 typedef struct GtkStyleInfo GtkStyleInfo;
 typedef struct GtkRegion GtkRegion;
@@ -444,7 +337,7 @@ struct GtkStyleInfo
 
 struct StyleData
 {
-  GtkStyleProperties *store;
+  GtkCssComputedValues *store;
   GSList *icon_factories;
   GArray *property_cache;
 };
@@ -454,6 +347,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;
@@ -462,33 +361,36 @@ struct AnimationInfo
   GArray *rectangles;
 };
 
-struct GtkStyleContextPrivate
+struct _GtkStyleContextPrivate
 {
   GdkScreen *screen;
 
   GList *providers;
   GList *providers_last;
 
+  GtkStyleContext *parent;
   GtkWidgetPath *widget_path;
   GHashTable *style_data;
   GSList *info_stack;
   StyleData *current_data;
+  GtkStateFlags current_state;
 
   GSList *animation_regions;
   GSList *animations;
 
-  guint animations_invalidated : 1;
-  guint invalidating_context : 1;
-
   GtkThemingEngine *theming_engine;
 
   GtkTextDirection direction;
+
+  guint animations_invalidated : 1;
+  guint invalidating_context : 1;
 };
 
 enum {
   PROP_0,
   PROP_SCREEN,
-  PROP_DIRECTION
+  PROP_DIRECTION,
+  PROP_PARENT
 };
 
 enum {
@@ -499,8 +401,6 @@ enum {
 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);
 
@@ -512,6 +412,9 @@ 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);
 
 
 G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
@@ -549,6 +452,21 @@ gtk_style_context_class_init (GtkStyleContextClass *klass)
                                                       GTK_TYPE_TEXT_DIRECTION,
                                                       GTK_TEXT_DIR_LTR,
                                                       GTK_PARAM_READWRITE));
+  /**
+   * GtkStyleContext:parent:
+   *
+   * Sets or gets the style context's parent. See gtk_style_context_set_parent()
+   * for details.
+   *
+   * Since: 3.4
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_PARENT,
+                                   g_param_spec_object ("parent",
+                                                        P_("Parent"),
+                                                        P_("The parent style context"),
+                                                        GTK_TYPE_STYLE_CONTEXT,
+                                                        GTK_PARAM_READWRITE));
 
   g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
 }
@@ -617,7 +535,7 @@ style_info_hash (gconstpointer elem)
       hash <<= 5;
     }
 
-  return hash;
+  return hash ^ info->state_flags;
 }
 
 static gboolean
@@ -629,6 +547,9 @@ style_info_equal (gconstpointer elem1,
   info1 = elem1;
   info2 = elem2;
 
+  if (info1->state_flags != info2->state_flags)
+    return FALSE;
+
   if (info1->junction_sides != info2->junction_sides)
     return FALSE;
 
@@ -657,7 +578,6 @@ style_data_new (void)
   StyleData *data;
 
   data = g_slice_new0 (StyleData);
-  data->store = gtk_style_properties_new ();
 
   return data;
 }
@@ -688,8 +608,7 @@ style_data_free (StyleData *data)
   g_object_unref (data->store);
   clear_property_cache (data);
 
-  g_slist_foreach (data->icon_factories, (GFunc) g_object_unref, NULL);
-  g_slist_free (data->icon_factories);
+  g_slist_free_full (data->icon_factories, g_object_unref);
 
   g_slice_free (StyleData, data);
 }
@@ -710,7 +629,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 ();
 
@@ -749,6 +668,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);
 }
 
@@ -904,16 +824,16 @@ gtk_style_context_finalize (GObject *object)
   style_context = GTK_STYLE_CONTEXT (object);
   priv = style_context->priv;
 
+  gtk_style_context_set_parent (style_context, NULL);
+
   if (priv->widget_path)
     gtk_widget_path_free (priv->widget_path);
 
   g_hash_table_destroy (priv->style_data);
 
-  g_list_foreach (priv->providers, (GFunc) style_provider_data_free, NULL);
-  g_list_free (priv->providers);
+  g_list_free_full (priv->providers, (GDestroyNotify) style_provider_data_free);
 
-  g_slist_foreach (priv->info_stack, (GFunc) style_info_free, NULL);
-  g_slist_free (priv->info_stack);
+  g_slist_free_full (priv->info_stack, (GDestroyNotify) style_info_free);
 
   g_slist_free (priv->animation_regions);
 
@@ -934,11 +854,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)
     {
@@ -950,6 +868,10 @@ gtk_style_context_impl_set_property (GObject      *object,
       gtk_style_context_set_direction (style_context,
                                        g_value_get_enum (value));
       break;
+    case PROP_PARENT:
+      gtk_style_context_set_parent (style_context,
+                                    g_value_get_object (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -962,8 +884,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;
@@ -976,6 +898,9 @@ gtk_style_context_impl_get_property (GObject    *object,
     case PROP_DIRECTION:
       g_value_set_enum (value, priv->direction);
       break;
+    case PROP_PARENT:
+      g_value_set_object (value, priv->parent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1010,18 +935,25 @@ find_next_candidate (GList    *local,
 static void
 build_properties (GtkStyleContext *context,
                   StyleData       *style_data,
-                  GtkWidgetPath   *path)
+                  GtkWidgetPath   *path,
+                  GtkStateFlags    state)
 {
   GtkStyleContextPrivate *priv;
   GList *elem, *list, *global_list = NULL;
+  GtkCssLookup *lookup;
 
   priv = context->priv;
-  list = priv->providers;
+  list = priv->providers_last;
 
   if (priv->screen)
-    global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
+    {
+      global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
+      global_list = g_list_last (global_list);
+    }
+
+  lookup = _gtk_css_lookup_new ();
 
-  while ((elem = find_next_candidate (list, global_list, TRUE)) != NULL)
+  while ((elem = find_next_candidate (list, global_list, FALSE)) != NULL)
     {
       GtkStyleProviderData *data;
       GtkStyleProperties *provider_style;
@@ -1029,18 +961,35 @@ build_properties (GtkStyleContext *context,
       data = elem->data;
 
       if (elem == list)
-        list = list->next;
+        list = list->prev;
       else
-        global_list = global_list->next;
-
-      provider_style = gtk_style_provider_get_style (data->provider, path);
+        global_list = global_list->prev;
 
-      if (provider_style)
+      if (GTK_IS_STYLE_PROVIDER_PRIVATE (data->provider))
         {
-          gtk_style_properties_merge (style_data->store, provider_style, TRUE);
-          g_object_unref (provider_style);
+          _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (data->provider),
+                                              path,
+                                              state,
+                                              lookup);
+        }
+      else
+        {
+          provider_style = gtk_style_provider_get_style (data->provider, path);
+
+          if (provider_style)
+            {
+              _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (provider_style),
+                                                  path,
+                                                  state,
+                                                  lookup);
+              g_object_unref (provider_style);
+            }
         }
     }
+
+  style_data->store = _gtk_css_computed_values_new ();
+  _gtk_css_lookup_resolve (lookup, context, style_data->store);
+  _gtk_css_lookup_free (lookup);
 }
 
 static void
@@ -1118,46 +1067,63 @@ create_query_path (GtkStyleContext *context)
 }
 
 static StyleData *
-style_data_lookup (GtkStyleContext *context)
+style_data_lookup (GtkStyleContext *context,
+                   GtkStateFlags    state)
 {
   GtkStyleContextPrivate *priv;
   StyleData *data;
+  gboolean state_mismatch;
+  const GValue *v;
 
   priv = context->priv;
+  state_mismatch = ((GtkStyleInfo *) priv->info_stack->data)->state_flags != state;
 
   /* Current data in use is cached, just return it */
-  if (priv->current_data)
+  if (priv->current_data && priv->current_state == state)
     return priv->current_data;
 
   g_assert (priv->widget_path != NULL);
 
-  data = g_hash_table_lookup (priv->style_data, priv->info_stack->data);
-
-  if (!data)
+  if (G_UNLIKELY (state_mismatch))
     {
-      GtkWidgetPath *path;
+      gtk_style_context_save (context);
+      gtk_style_context_set_state (context, state);
+    }
 
-      data = style_data_new ();
-      path = create_query_path (context);
+  priv->current_data = g_hash_table_lookup (priv->style_data, priv->info_stack->data);
+  priv->current_state = state;
 
-      build_properties (context, data, path);
-      build_icon_factories (context, data, path);
+  if (!priv->current_data)
+    {
+      GtkWidgetPath *path;
 
+      priv->current_data = style_data_new ();
       g_hash_table_insert (priv->style_data,
                            style_info_copy (priv->info_stack->data),
-                           data);
+                           priv->current_data);
+
+      path = create_query_path (context);
+
+      build_properties (context, priv->current_data, path, state);
+      build_icon_factories (context, priv->current_data, path);
 
       gtk_widget_path_free (path);
     }
 
-  priv->current_data = data;
+  data = priv->current_data;
 
   if (priv->theming_engine)
     g_object_unref (priv->theming_engine);
 
-  gtk_style_properties_get (data->store, 0,
-                            "engine", &priv->theming_engine,
-                            NULL);
+  v = _gtk_css_computed_values_get_value_by_name (priv->current_data->store, "engine");
+  if (v)
+    priv->theming_engine = g_value_dup_object (v);
+  else
+    priv->theming_engine = g_object_ref (gtk_theming_engine_load (NULL));
+
+  if (G_UNLIKELY (state_mismatch))
+    gtk_style_context_restore (context);
+
   return data;
 }
 
@@ -1271,6 +1237,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
@@ -1369,6 +1339,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
@@ -1425,6 +1399,55 @@ gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
     }
 }
 
+/**
+ * gtk_style_context_get_section:
+ * @context: a #GtkStyleContext
+ * @property: style property name
+ *
+ * Queries the location in the CSS where @property was defined for the
+ * current @context. Note that the state to be queried is taken from
+ * gtk_style_context_get_state().
+ *
+ * If the location is not available, %NULL will be returned. The
+ * location might not be available for various reasons, such as the
+ * property being overridden, @property not naming a supported CSS
+ * property or tracking of definitions being disabled for performance
+ * reasons.
+ *
+ * Shorthand CSS properties cannot be queried for a location and will
+ * always return %NULL.
+ *
+ * Returns: %NULL or the section where value was defined
+ **/
+GtkCssSection *
+gtk_style_context_get_section (GtkStyleContext *context,
+                               const gchar     *property)
+{
+  GtkStyleContextPrivate *priv;
+  GtkStyleProperty *prop;
+  StyleData *data;
+
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+  g_return_val_if_fail (property != NULL, NULL);
+
+  priv = context->priv;
+  g_return_val_if_fail (priv->widget_path != NULL, NULL);
+
+  prop = _gtk_style_property_lookup (property);
+  if (!GTK_IS_CSS_STYLE_PROPERTY (prop))
+    return NULL;
+
+  data = style_data_lookup (context, gtk_style_context_get_state (context));
+  return _gtk_css_computed_values_get_section (data->store, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)));
+}
+
+static const GValue *
+gtk_style_context_query_func (guint    id,
+                              gpointer values)
+{
+  return _gtk_css_computed_values_get_value (values, id);
+}
+
 /**
  * gtk_style_context_get_property:
  * @context: a #GtkStyleContext
@@ -1446,6 +1469,7 @@ gtk_style_context_get_property (GtkStyleContext *context,
                                 GValue          *value)
 {
   GtkStyleContextPrivate *priv;
+  GtkStyleProperty *prop;
   StyleData *data;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
@@ -1453,11 +1477,22 @@ gtk_style_context_get_property (GtkStyleContext *context,
   g_return_if_fail (value != NULL);
 
   priv = context->priv;
-
   g_return_if_fail (priv->widget_path != NULL);
 
-  data = style_data_lookup (context);
-  gtk_style_properties_get_property (data->store, property, state, value);
+  prop = _gtk_style_property_lookup (property);
+  if (prop == NULL)
+    {
+      g_warning ("Style property \"%s\" is not registered", property);
+      return;
+    }
+  if (_gtk_style_property_get_value_type (prop) == G_TYPE_NONE)
+    {
+      g_warning ("Style property \"%s\" is not gettable", property);
+      return;
+    }
+
+  data = style_data_lookup (context, state);
+  _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
 }
 
 /**
@@ -1475,16 +1510,34 @@ gtk_style_context_get_valist (GtkStyleContext *context,
                               GtkStateFlags    state,
                               va_list          args)
 {
-  GtkStyleContextPrivate *priv;
-  StyleData *data;
+  const gchar *property_name;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  priv = context->priv;
-  g_return_if_fail (priv->widget_path != NULL);
+  property_name = va_arg (args, const gchar *);
+
+  while (property_name)
+    {
+      gchar *error = NULL;
+      GValue value = G_VALUE_INIT;
+
+      gtk_style_context_get_property (context,
+                                      property_name,
+                                      state,
+                                      &value);
+
+      G_VALUE_LCOPY (&value, args, 0, &error);
+      g_value_unset (&value);
 
-  data = style_data_lookup (context);
-  gtk_style_properties_get_valist (data->store, state, args);
+      if (error)
+        {
+          g_warning ("Could not get style property \"%s\": %s", property_name, error);
+          g_free (error);
+          break;
+        }
+
+      property_name = va_arg (args, const gchar *);
+    }
 }
 
 /**
@@ -1503,19 +1556,12 @@ gtk_style_context_get (GtkStyleContext *context,
                        GtkStateFlags    state,
                        ...)
 {
-  GtkStyleContextPrivate *priv;
-  StyleData *data;
   va_list args;
 
   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);
-
   va_start (args, state);
-  gtk_style_properties_get_valist (data->store, state, args);
+  gtk_style_context_get_valist (context, state, args);
   va_end (args);
 }
 
@@ -1572,7 +1618,6 @@ context_has_animatable_region (GtkStyleContext *context,
                                gpointer         region_id)
 {
   GtkStyleContextPrivate *priv;
-  GSList *r;
 
   /* NULL region_id means everything
    * rendered through the style context
@@ -1581,14 +1626,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;
 }
 
 /**
@@ -1688,7 +1726,7 @@ gtk_style_context_set_path (GtkStyleContext *context,
  *
  * Since: 3.0
  **/
-G_CONST_RETURN GtkWidgetPath *
+const GtkWidgetPath *
 gtk_style_context_get_path (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
@@ -1697,6 +1735,66 @@ gtk_style_context_get_path (GtkStyleContext *context)
   return priv->widget_path;
 }
 
+/**
+ * gtk_style_context_set_parent:
+ * @context: a #GtkStyleContext
+ * @parent: (allow-none): the new parent or %NULL
+ *
+ * Sets the parent style context for @context. The parent style
+ * context is used to implement
+ * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance">inheritance</ulink>
+ * of properties.
+ *
+ * If you are using a #GtkStyleContext returned from
+ * gtk_widget_get_style_context(), the parent will be set for you.
+ *
+ * Since: 3.4
+ **/
+void
+gtk_style_context_set_parent (GtkStyleContext *context,
+                              GtkStyleContext *parent)
+{
+  GtkStyleContextPrivate *priv;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (parent == NULL || GTK_IS_STYLE_CONTEXT (parent));
+
+  priv = context->priv;
+
+  if (priv->parent == parent)
+    return;
+
+  if (parent)
+    g_object_ref (parent);
+
+  if (priv->parent)
+    g_object_unref (priv->parent);
+
+  priv->parent = parent;
+
+  g_object_notify (G_OBJECT (context), "parent");
+  gtk_style_context_invalidate (context);
+}
+
+/**
+ * gtk_style_context_get_parent:
+ * @context: a #GtkStyleContext
+ *
+ * Gets the parent context set via gtk_style_context_set_parent().
+ * See that function for details.
+ *
+ * Returns: (transfer none): the parent context or %NULL
+ *
+ * Since: 3.4
+ **/
+GtkStyleContext *
+gtk_style_context_get_parent (GtkStyleContext *context)
+{
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+  return context->priv->parent;
+}
+
 /**
  * gtk_style_context_save:
  * @context: a #GtkStyleContext
@@ -2066,6 +2164,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
@@ -2086,12 +2204,15 @@ gtk_style_context_list_regions (GtkStyleContext *context)
  * Pseudo-classes are used for matching @flags, so the two
  * following rules:
  * <programlisting>
- * GtkTreeView row:nth-child (even) { ... }
- * GtkTreeView row:nth-child (odd) { ... }
+ * GtkTreeView row:nth-child(even) { ... }
+ * GtkTreeView row:nth-child(odd) { ... }
  * </programlisting>
  *
  * 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
@@ -2106,6 +2227,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);
@@ -2241,6 +2363,15 @@ style_property_values_cmp (gconstpointer bsearch_node1,
   return 0;
 }
 
+const GValue *
+_gtk_style_context_peek_property (GtkStyleContext *context,
+                                  const char      *property_name)
+{
+  StyleData *data = style_data_lookup (context, gtk_style_context_get_state (context));
+
+  return _gtk_css_computed_values_get_value_by_name (data->store, property_name);
+}
+
 const GValue *
 _gtk_style_context_peek_style_property (GtkStyleContext *context,
                                         GType            widget_type,
@@ -2254,7 +2385,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
   guint i;
 
   priv = context->priv;
-  data = style_data_lookup (context);
+  data = style_data_lookup (context, state);
 
   key.widget_type = widget_type;
   key.state = state;
@@ -2318,17 +2449,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);
 
-                  if (gtk_symbolic_color_resolve (color, data->store, &rgba))
-                    {
-                      g_value_unset (&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_style_context_resolve_color (context, color, &rgba))
+                    {
                       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;
@@ -2337,12 +2470,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;
@@ -2360,7 +2494,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.
  *
@@ -2552,7 +2686,7 @@ gtk_style_context_lookup_icon_set (GtkStyleContext *context,
   priv = context->priv;
   g_return_val_if_fail (priv->widget_path != NULL, NULL);
 
-  data = style_data_lookup (context);
+  data = style_data_lookup (context, 0);
 
   for (list = data->icon_factories; list; list = list->next)
     {
@@ -2611,7 +2745,7 @@ gtk_style_context_set_screen (GtkStyleContext *context,
  *
  * Returns the #GdkScreen to which @context is attached.
  *
- * Returns: a #GdkScreen.
+ * Returns: (transfer none): a #GdkScreen.
  **/
 GdkScreen *
 gtk_style_context_get_screen (GtkStyleContext *context)
@@ -2727,39 +2861,92 @@ gtk_style_context_get_junction_sides (GtkStyleContext *context)
   return info->junction_sides;
 }
 
-/**
- * gtk_style_context_lookup_color:
- * @context: a #GtkStyleContext
- * @color_name: color name to lookup
- * @color: (out): Return location for the looked up color
- *
- * Looks up and resolves a color name in the @context color map.
- *
- * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise
- **/
+static GtkSymbolicColor *
+gtk_style_context_color_lookup_func (gpointer    contextp,
+                                     const char *name)
+{
+  GtkSymbolicColor *sym_color;
+  GtkStyleContext *context = contextp;
+  GtkStyleContextPrivate *priv = context->priv;
+  GList *elem, *list, *global_list = NULL;
+
+  list = priv->providers_last;
+  if (priv->screen)
+    {
+      global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
+      global_list = g_list_last (global_list);
+    }
+
+  sym_color = NULL;
+  
+  while (sym_color == NULL &&
+         (elem = find_next_candidate (list, global_list, FALSE)) != NULL)
+    {
+      GtkStyleProviderData *data;
+
+      data = elem->data;
+
+      if (elem == list)
+        list = list->prev;
+      else
+        global_list = global_list->prev;
+
+      if (GTK_IS_STYLE_PROVIDER_PRIVATE (data->provider))
+        {
+          sym_color = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (data->provider),
+                                                             name);
+        }
+      else
+        {
+          /* If somebody hits this code path, shout at them */
+          sym_color = NULL;
+        }
+    }
+
+  return sym_color;
+}
+
+gboolean
+_gtk_style_context_resolve_color (GtkStyleContext  *context,
+                                  GtkSymbolicColor *color,
+                                  GdkRGBA          *result)
+{
+  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
+  g_return_val_if_fail (color != NULL, FALSE);
+  g_return_val_if_fail (result != NULL, FALSE);
+
+  return _gtk_symbolic_color_resolve_full (color,
+                                           gtk_style_context_color_lookup_func,
+                                           context,
+                                           result);
+}
+
+/**
+ * gtk_style_context_lookup_color:
+ * @context: a #GtkStyleContext
+ * @color_name: color name to lookup
+ * @color: (out): Return location for the looked up color
+ *
+ * Looks up and resolves a color name in the @context color map.
+ *
+ * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise
+ **/
 gboolean
 gtk_style_context_lookup_color (GtkStyleContext *context,
                                 const gchar     *color_name,
                                 GdkRGBA         *color)
 {
-  GtkStyleContextPrivate *priv;
   GtkSymbolicColor *sym_color;
-  StyleData *data;
 
   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);
 
-  priv = context->priv;
-  g_return_val_if_fail (priv->widget_path != NULL, FALSE);
-
-  data = style_data_lookup (context);
-  sym_color = gtk_style_properties_lookup_color (data->store, color_name);
-
-  if (!sym_color)
+  sym_color = gtk_style_context_color_lookup_func (context, color_name);
+  if (sym_color == NULL)
     return FALSE;
 
-  return gtk_symbolic_color_resolve (sym_color, data->store, color);
+  return _gtk_style_context_resolve_color (context, sym_color, color);
 }
 
 /**
@@ -2826,6 +3013,7 @@ gtk_style_context_notify_state_change (GtkStyleContext *context,
   GtkAnimationDescription *desc;
   AnimationInfo *info;
   GtkStateFlags flags;
+  const GValue *v;
   StyleData *data;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
@@ -2866,19 +3054,16 @@ gtk_style_context_notify_state_change (GtkStyleContext *context,
   /* Find out if there is any animation description for the given
    * state, it will fallback to the normal state as well if necessary.
    */
-  data = style_data_lookup (context);
-  gtk_style_properties_get (data->store, flags,
-                            "transition", &desc,
-                            NULL);
-
+  data = style_data_lookup (context, flags);
+  v = _gtk_css_computed_values_get_value_by_name (data->store, "transition");
+  if (!v)
+    return;
+  desc = g_value_get_boxed (v);
   if (!desc)
     return;
 
   if (_gtk_animation_description_get_duration (desc) == 0)
-    {
-      _gtk_animation_description_unref (desc);
-      return;
-    }
+    return;
 
   info = animation_info_lookup (context, region_id, state);
 
@@ -2915,8 +3100,122 @@ gtk_style_context_notify_state_change (GtkStyleContext *context,
       priv->animations = g_slist_prepend (priv->animations, info);
       priv->animations_invalidated = TRUE;
     }
+}
+
+/**
+ * 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);
+    }
 
-  _gtk_animation_description_unref (desc);
+  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);
+    }
 }
 
 /**
@@ -3001,7 +3300,7 @@ _gtk_style_context_invalidate_animation_areas (GtkStyleContext *context)
 
 void
 _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
-                                            GtkWidget       *widget)
+                                             GtkWidget       *widget)
 {
   GtkStyleContextPrivate *priv;
   GSList *l;
@@ -3027,13 +3326,8 @@ _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);
@@ -3043,8 +3337,12 @@ _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
           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);
         }
@@ -3092,6 +3390,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);
+            }
         }
     }
 }
@@ -3189,28 +3495,18 @@ 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);
+  gtk_style_context_get (context,
+                         state,
+                         "color", &c,
+                         NULL);
 
-  if (value)
-    {
-      c = g_value_get_boxed (value);
-      *color = *c;
-    }
+  *color = *c;
+  gdk_rgba_free (c);
 }
 
 /**
@@ -3228,28 +3524,18 @@ 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);
+  gtk_style_context_get (context,
+                         state,
+                         "background-color", &c,
+                         NULL);
 
-  if (value)
-    {
-      c = g_value_get_boxed (value);
-      *color = *c;
-    }
+  *color = *c;
+  gdk_rgba_free (c);
 }
 
 /**
@@ -3267,28 +3553,18 @@ 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);
+  gtk_style_context_get (context,
+                         state,
+                         "border-color", &c,
+                         NULL);
 
-  if (value)
-    {
-      c = g_value_get_boxed (value);
-      *color = *c;
-    }
+  *color = *c;
+  gdk_rgba_free (c);
 }
 
 /**
@@ -3298,6 +3574,7 @@ gtk_style_context_get_border_color (GtkStyleContext *context,
  * @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
  **/
@@ -3306,28 +3583,23 @@ gtk_style_context_get_border (GtkStyleContext *context,
                               GtkStateFlags    state,
                               GtkBorder       *border)
 {
-  GtkStyleContextPrivate *priv;
-  StyleData *data;
-  const GValue *value;
-  GtkBorder *b;
+  int top, left, bottom, right;
 
   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);
+  gtk_style_context_get (context,
+                         state,
+                         "border-top-width", &top,
+                         "border-left-width", &left,
+                         "border-bottom-width", &bottom,
+                         "border-right-width", &right,
+                         NULL);
 
-  if (value)
-    {
-      b = g_value_get_boxed (value);
-      *border = *b;
-    }
+  border->top = top;
+  border->left = left;
+  border->bottom = bottom;
+  border->right = right;
 }
 
 /**
@@ -3337,6 +3609,7 @@ gtk_style_context_get_border (GtkStyleContext *context,
  * @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
  **/
@@ -3345,28 +3618,23 @@ gtk_style_context_get_padding (GtkStyleContext *context,
                                GtkStateFlags    state,
                                GtkBorder       *padding)
 {
-  GtkStyleContextPrivate *priv;
-  StyleData *data;
-  const GValue *value;
-  GtkBorder *b;
+  int top, left, bottom, right;
 
   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);
+  gtk_style_context_get (context,
+                         state,
+                         "padding-top", &top,
+                         "padding-left", &left,
+                         "padding-bottom", &bottom,
+                         "padding-right", &right,
+                         NULL);
 
-  if (value)
-    {
-      b = g_value_get_boxed (value);
-      *padding = *b;
-    }
+  padding->top = top;
+  padding->left = left;
+  padding->bottom = bottom;
+  padding->right = right;
 }
 
 /**
@@ -3376,6 +3644,7 @@ gtk_style_context_get_padding (GtkStyleContext *context,
  * @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
  **/
@@ -3384,28 +3653,23 @@ gtk_style_context_get_margin (GtkStyleContext *context,
                               GtkStateFlags    state,
                               GtkBorder       *margin)
 {
-  GtkStyleContextPrivate *priv;
-  StyleData *data;
-  const GValue *value;
-  GtkBorder *b;
+  int top, left, bottom, right;
 
   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);
+  gtk_style_context_get (context,
+                         state,
+                         "margin-top", &top,
+                         "margin-left", &left,
+                         "margin-bottom", &bottom,
+                         "margin-right", &right,
+                         NULL);
 
-  if (value)
-    {
-      b = g_value_get_boxed (value);
-      *margin = *b;
-    }
+  margin->top = top;
+  margin->left = left;
+  margin->bottom = bottom;
+  margin->right = right;
 }
 
 /**
@@ -3417,8 +3681,9 @@ gtk_style_context_get_margin (GtkStyleContext *context,
  * object is const and will remain valid until the
  * #GtkStyleContext::changed signal happens.
  *
- * Returns: the #PangoFontDescription for the given state. This
- *          object is owned by GTK+ and should not be freed.
+ * Returns: (transfer none): the #PangoFontDescription for the given
+ *          state.  This object is owned by GTK+ and should not be
+ *          freed.
  *
  * Since: 3.0
  **/
@@ -3428,20 +3693,77 @@ gtk_style_context_get_font (GtkStyleContext *context,
 {
   GtkStyleContextPrivate *priv;
   StyleData *data;
-  const GValue *value;
+  PangoFontDescription *description;
 
   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);
+  data = style_data_lookup (context, state);
 
-  if (value)
-    return g_value_get_boxed (value);
+  /* 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);
+      g_object_set_data_full (G_OBJECT (data->store),
+                              "font-cache-for-get_font",
+                              description,
+                              (GDestroyNotify) pango_font_description_free);
+    }
+  return description;
+}
 
-  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.0;
+      color->green = style_color->green / 65535.0;
+      color->blue = style_color->blue / 65535.0;
+      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 */
@@ -3481,17 +3803,22 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3527,17 +3854,22 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3545,8 +3877,8 @@ gtk_render_option (GtkStyleContext *context,
  * @context: a #GtkStyleContext
  * @cr: a #cairo_t
  * @angle: arrow angle from 0 to 2 * %G_PI, being 0 the arrow pointing to the north
- * @x: Center X for the render area
- * @y: Center Y for the render area
+ * @x: X origin of the render area
+ * @y: Y origin of the render area
  * @size: square side for render area
  *
  * Renders an arrow pointing to @angle.
@@ -3571,16 +3903,26 @@ 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);
+
+  if (size <= 0)
+    return;
 
   priv = context->priv;
   engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
 
+  cairo_save (cr);
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW);
+
   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);
+
+  gtk_style_context_restore (context);
+  cairo_restore (cr);
 }
 
 /**
@@ -3617,16 +3959,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3665,16 +4012,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3710,16 +4062,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3752,16 +4109,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3794,6 +4156,8 @@ gtk_render_layout (GtkStyleContext *context,
   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,
@@ -3804,6 +4168,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);
 }
 
 /**
@@ -3836,8 +4202,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);
 }
 
 /**
@@ -3875,16 +4245,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -3927,11 +4302,12 @@ 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 <= xy1_gap);
   g_return_if_fail (xy0_gap >= 0);
 
+  if (width <= 0 || height <= 0)
+    return;
+
   if (gap_side == GTK_POS_LEFT ||
       gap_side == GTK_POS_RIGHT)
     g_return_if_fail (xy1_gap <= height);
@@ -3941,12 +4317,16 @@ gtk_render_frame_gap (GtkStyleContext *context,
   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);
 }
 
 /**
@@ -3984,16 +4364,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -4029,16 +4414,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -4069,16 +4459,21 @@ 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);
+
+  if (width <= 0 || height <= 0)
+    return;
 
   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);
 }
 
 /**
@@ -4104,7 +4499,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;
@@ -4113,3 +4508,301 @@ gtk_render_icon_pixbuf (GtkStyleContext     *context,
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   return engine_class->render_icon_pixbuf (priv->theming_engine, source, size);
 }
+
+/**
+ * gtk_render_icon:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @pixbuf: a #GdkPixbuf containing the icon to draw
+ * @x: X position for the @pixbuf
+ * @y: Y position for the @pixbuf
+ *
+ * Renders the icon in @pixbuf at the specified @x and @y coordinates.
+ *
+ * Since: 3.2
+ **/
+void
+gtk_render_icon (GtkStyleContext *context,
+                 cairo_t         *cr,
+                 GdkPixbuf       *pixbuf,
+                 gdouble          x,
+                 gdouble          y)
+{
+  GtkStyleContextPrivate *priv;
+  GtkThemingEngineClass *engine_class;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (cr != NULL);
+
+  priv = context->priv;
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+  cairo_save (cr);
+
+  store_animation_region (context,
+                          x, y,
+                          gdk_pixbuf_get_width (pixbuf),
+                          gdk_pixbuf_get_height (pixbuf));
+
+  _gtk_theming_engine_set_context (priv->theming_engine, context);
+  engine_class->render_icon (priv->theming_engine, cr, pixbuf, x, y);
+
+  cairo_restore (cr);
+}
+
+static void
+draw_insertion_cursor (GtkStyleContext *context,
+                       cairo_t         *cr,
+                       gdouble          x,
+                       gdouble          y,
+                       gdouble          height,
+                       gboolean         is_primary,
+                       PangoDirection   direction,
+                       gboolean         draw_arrow)
+
+{
+  GdkRGBA primary_color;
+  GdkRGBA secondary_color;
+  gfloat cursor_aspect_ratio;
+  gint stem_width;
+  gint offset;
+
+  cairo_save (cr);
+
+  _gtk_style_context_get_cursor_color (context, &primary_color, &secondary_color);
+  gdk_cairo_set_source_rgba (cr, is_primary ? &primary_color : &secondary_color);
+
+  /* When changing the shape or size of the cursor here,
+   * propagate the changes to gtktextview.c:text_window_invalidate_cursors().
+   */
+
+  gtk_style_context_get_style (context,
+                               "cursor-aspect-ratio", &cursor_aspect_ratio,
+                               NULL);
+
+  stem_width = height * cursor_aspect_ratio + 1;
+
+  /* put (stem_width % 2) on the proper side of the cursor */
+  if (direction == PANGO_DIRECTION_LTR)
+    offset = stem_width / 2;
+  else
+    offset = stem_width - stem_width / 2;
+
+  cairo_rectangle (cr, x - offset, y, stem_width, height);
+  cairo_fill (cr);
+
+  if (draw_arrow)
+    {
+      gint arrow_width;
+      gint ax, ay;
+
+      arrow_width = stem_width + 1;
+
+      if (direction == PANGO_DIRECTION_RTL)
+        {
+          ax = x - offset - 1;
+          ay = y + height - arrow_width * 2 - arrow_width + 1;
+
+          cairo_move_to (cr, ax, ay + 1);
+          cairo_line_to (cr, ax - arrow_width, ay + arrow_width);
+          cairo_line_to (cr, ax, ay + 2 * arrow_width);
+          cairo_fill (cr);
+        }
+      else if (direction == PANGO_DIRECTION_LTR)
+        {
+          ax = x + stem_width - offset;
+          ay = y + height - arrow_width * 2 - arrow_width + 1;
+
+          cairo_move_to (cr, ax, ay + 1);
+          cairo_line_to (cr, ax + arrow_width, ay + arrow_width);
+          cairo_line_to (cr, ax, ay + 2 * arrow_width);
+          cairo_fill (cr);
+        }
+      else
+        g_assert_not_reached();
+    }
+
+  cairo_restore (cr);
+}
+
+/**
+ * gtk_render_insertion_cursor:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin
+ * @y: Y origin
+ * @layout: the #PangoLayout of the text
+ * @index: the index in the #PangoLayout
+ * @direction: the #PangoDirection of the text
+ *
+ * Draws a text caret on @cr at the specified index of @layout.
+ *
+ * Since: 3.4
+ **/
+void
+gtk_render_insertion_cursor (GtkStyleContext *context,
+                             cairo_t         *cr,
+                             gdouble          x,
+                             gdouble          y,
+                             PangoLayout     *layout,
+                             int              index,
+                             PangoDirection   direction)
+{
+  GtkStyleContextPrivate *priv;
+  gboolean split_cursor;
+  PangoRectangle strong_pos, weak_pos;
+  PangoRectangle *cursor1, *cursor2;
+  PangoDirection keymap_direction;
+  PangoDirection direction2;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (cr != NULL);
+  g_return_if_fail (PANGO_IS_LAYOUT (layout));
+  g_return_if_fail (index >= 0);
+
+  priv = context->priv;
+
+  g_object_get (gtk_settings_get_for_screen (priv->screen),
+                "gtk-split-cursor", &split_cursor,
+                NULL);
+
+  keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gdk_screen_get_display (priv->screen)));
+
+  pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
+
+  direction2 = PANGO_DIRECTION_NEUTRAL;
+
+  if (split_cursor)
+    {
+      cursor1 = &strong_pos;
+
+      if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y)
+        {
+          direction2 = (direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
+          cursor2 = &weak_pos;
+        }
+    }
+  else
+    {
+      if (keymap_direction == direction)
+        cursor1 = &strong_pos;
+      else
+        cursor1 = &weak_pos;
+    }
+
+  draw_insertion_cursor (context,
+                         cr,
+                         x + PANGO_PIXELS (cursor1->x),
+                         y + PANGO_PIXELS (cursor1->y),
+                         PANGO_PIXELS (cursor1->height),
+                         TRUE,
+                         direction,
+                         direction2 != PANGO_DIRECTION_NEUTRAL);
+
+  if (direction2 != PANGO_DIRECTION_NEUTRAL)
+    {
+      draw_insertion_cursor (context,
+                             cr,
+                             x + PANGO_PIXELS (cursor2->x),
+                             y + PANGO_PIXELS (cursor2->y),
+                             PANGO_PIXELS (cursor2->height),
+                             FALSE,
+                             direction2,
+                             TRUE);
+    }
+}
+
+/**
+ * gtk_draw_insertion_cursor:
+ * @widget:  a #GtkWidget
+ * @cr: cairo context to draw to
+ * @location: location where to draw the cursor (@location->width is ignored)
+ * @is_primary: if the cursor should be the primary cursor color.
+ * @direction: whether the cursor is left-to-right or
+ *             right-to-left. Should never be #GTK_TEXT_DIR_NONE
+ * @draw_arrow: %TRUE to draw a directional arrow on the
+ *        cursor. Should be %FALSE unless the cursor is split.
+ *
+ * Draws a text caret on @cr at @location. This is not a style function
+ * but merely a convenience function for drawing the standard cursor shape.
+ *
+ * Since: 3.0
+ */
+void
+gtk_draw_insertion_cursor (GtkWidget          *widget,
+                           cairo_t            *cr,
+                           const GdkRectangle *location,
+                           gboolean            is_primary,
+                           GtkTextDirection    direction,
+                           gboolean            draw_arrow)
+{
+  GtkStyleContext *context;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (cr != NULL);
+  g_return_if_fail (location != NULL);
+  g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
+
+  context = gtk_widget_get_style_context (widget);
+
+  draw_insertion_cursor (context, cr,
+                         location->x, location->y, location->height,
+                         is_primary,
+                         (direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR,
+                         draw_arrow);
+}
+
+static AtkAttributeSet *
+add_attribute (AtkAttributeSet  *attributes,
+               AtkTextAttribute  attr,
+               const gchar      *value)
+{
+  AtkAttribute *at;
+
+  at = g_new (AtkAttribute, 1);
+  at->name = g_strdup (atk_text_attribute_get_name (attr));
+  at->value = g_strdup (value);
+
+  return g_slist_prepend (attributes, at);
+}
+
+/*
+ * _gtk_style_context_get_attributes:
+ * @attributes: a #AtkAttributeSet to add attributes to
+ * @context: the #GtkStyleContext to get attributes from
+ * @flags: the state to use with @context
+ *
+ * Adds the foreground and background color from @context to
+ * @attributes, after translating them to ATK attributes.
+ *
+ * This is a convenience function that can be used in
+ * implementing the #AtkText interface in widgets.
+ *
+ * Returns: the modified #AtkAttributeSet
+ */
+AtkAttributeSet *
+_gtk_style_context_get_attributes (AtkAttributeSet *attributes,
+                                   GtkStyleContext *context,
+                                   GtkStateFlags    flags)
+{
+  GdkRGBA color;
+  gchar *value;
+
+  gtk_style_context_get_background_color (context, flags, &color);
+  value = g_strdup_printf ("%u,%u,%u",
+                           (guint) ceil (color.red * 65536 - color.red),
+                           (guint) ceil (color.green * 65536 - color.green),
+                           (guint) ceil (color.blue * 65536 - color.blue));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
+  g_free (value);
+
+  gtk_style_context_get_color (context, flags, &color);
+  value = g_strdup_printf ("%u,%u,%u",
+                           (guint) ceil (color.red * 65536 - color.red),
+                           (guint) ceil (color.green * 65536 - color.green),
+                           (guint) ceil (color.blue * 65536 - color.blue));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
+  g_free (value);
+
+  return attributes;
+}