]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtklabel.c
Merge branch 'bgo593793-filechooser-recent-folders-master'
[~andy/gtk] / gtk / gtklabel.c
index 94ac98a0367f27e014c9a55098fcee4e41793abb..0b7ee8833235b8391f193fb5aa8cd15b389fd246 100644 (file)
 #include "gtklabel.h"
 #include "gtkaccellabel.h"
 #include "gtkdnd.h"
-#include "gtkmain.h"
+#include "gtkmainprivate.h"
 #include "gtkmarshalers.h"
 #include "gtkpango.h"
 #include "gtkwindow.h"
-#include "gdk/gdkkeysyms.h"
 #include "gtkclipboard.h"
 #include "gtkimagemenuitem.h"
 #include "gtkintl.h"
 #include "gtkseparatormenuitem.h"
 #include "gtktextutil.h"
 #include "gtkmenuitem.h"
+#include "gtkmenushellprivate.h"
 #include "gtknotebook.h"
 #include "gtkstock.h"
 #include "gtkbindings.h"
 #include "gtkimage.h"
 #include "gtkshow.h"
 #include "gtktooltip.h"
-#include "gtksizerequest.h"
 #include "gtkprivate.h"
+#include "gtktypebuiltins.h"
 
+#include "a11y/gtklabelaccessible.h"
 
-struct _GtkLabelPriv
+/**
+ * SECTION:gtklabel
+ * @Short_description: A widget that displays a small to medium amount of text
+ * @Title: GtkLabel
+ *
+ * The #GtkLabel widget displays a small amount of text. As the name
+ * implies, most labels are used to label another widget such as a
+ * #GtkButton, a #GtkMenuItem, or a #GtkOptionMenu.
+ *
+ * <refsect2 id="GtkLabel-BUILDER-UI">
+ * <title>GtkLabel as GtkBuildable</title>
+ * <para>
+ * The GtkLabel implementation of the GtkBuildable interface supports a
+ * custom &lt;attributes&gt; element, which supports any number of &lt;attribute&gt;
+ * elements. the &lt;attribute&gt; element has attributes named name, value,
+ * start and end and allows you to specify #PangoAttribute values for this label.
+ *
+ * <example>
+ * <title>A UI definition fragment specifying Pango attributes</title>
+ * <programlisting><![CDATA[
+ * <object class="GtkLabel">
+ *   <attributes>
+ *     <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ *     <attribute name="background" value="red" start="5" end="10"/>"
+ *   </attributes>
+ * </object>
+ * ]]></programlisting>
+ * </example>
+ * The start and end attributes specify the range of characters to which the
+ * Pango attribute applies. If start and end are not specified, the attribute is
+ * applied to the whole text. Note that specifying ranges does not make much
+ * sense with translatable attributes. Use markup embedded in the translatable
+ * content instead.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Mnemonics</title>
+ * <para>
+ * Labels may contain <firstterm>mnemonics</firstterm>. Mnemonics are
+ * underlined characters in the label, used for keyboard navigation.
+ * Mnemonics are created by providing a string with an underscore before
+ * the mnemonic character, such as <literal>"_File"</literal>, to the
+ * functions gtk_label_new_with_mnemonic() or
+ * gtk_label_set_text_with_mnemonic().
+ *
+ * Mnemonics automatically activate any activatable widget the label is
+ * inside, such as a #GtkButton; if the label is not inside the
+ * mnemonic's target widget, you have to tell the label about the target
+ * using gtk_label_set_mnemonic_widget(). Here's a simple example where
+ * the label is inside a button:
+ *
+ * <informalexample>
+ * <programlisting>
+ *   // Pressing Alt+H will activate this button
+ *   button = gtk_button_new (<!-- -->);
+ *   label = gtk_label_new_with_mnemonic ("_Hello");
+ *   gtk_container_add (GTK_CONTAINER (button), label);
+ * </programlisting>
+ * </informalexample>
+ *
+ * There's a convenience function to create buttons with a mnemonic label
+ * already inside:
+ *
+ * <informalexample>
+ * <programlisting>
+ *   // Pressing Alt+H will activate this button
+ *   button = gtk_button_new_with_mnemonic ("_Hello");
+ * </programlisting>
+ * </informalexample>
+ *
+ * To create a mnemonic for a widget alongside the label, such as a
+ * #GtkEntry, you have to point the label at the entry with
+ * gtk_label_set_mnemonic_widget():
+ *
+ * <informalexample>
+ * <programlisting>
+ *   // Pressing Alt+H will focus the entry
+ *   entry = gtk_entry_new (<!-- -->);
+ *   label = gtk_label_new_with_mnemonic ("_Hello");
+ *   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+ * </programlisting>
+ * </informalexample>
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Markup (styled text)</title>
+ * <para>
+ * To make it easy to format text in a label (changing colors, fonts,
+ * etc.), label text can be provided in a simple <link
+ * linkend="PangoMarkupFormat">markup format</link>.
+ * Here's how to create a label with a small font:
+ *
+ * <informalexample>
+ * <programlisting>
+ *   label = gtk_label_new (NULL);
+ *   gtk_label_set_markup (GTK_LABEL (label), "<small>Small text</small>");
+ * </programlisting>
+ * </informalexample>
+ *
+ * (See <link
+ * linkend="PangoMarkupFormat">complete documentation</link> of available
+ * tags in the Pango manual.)
+ *
+ * The markup passed to gtk_label_set_markup() must be valid; for example,
+ * literal &lt;, &gt; and &amp; characters must be escaped as \&lt;,
+ * \gt;, and \&amp;. If you pass text obtained from the user, file,
+ * or a network to gtk_label_set_markup(), you'll want to escape it with
+ * g_markup_escape_text() or g_markup_printf_escaped().
+ *
+ * Markup strings are just a convenient way to set the #PangoAttrList on
+ * a label; gtk_label_set_attributes() may be a simpler way to set
+ * attributes in some cases. Be careful though; #PangoAttrList tends to
+ * cause internationalization problems, unless you're applying attributes
+ * to the entire string (i.e. unless you set the range of each attribute
+ * to [0, %G_MAXINT)). The reason is that specifying the start_index and
+ * end_index for a #PangoAttribute requires knowledge of the exact string
+ * being displayed, so translations will cause problems.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Selectable labels</title>
+ * Labels can be made selectable with gtk_label_set_selectable().
+ * Selectable labels allow the user to copy the label contents to
+ * the clipboard. Only labels that contain useful-to-copy information
+ * &mdash; such as error messages &mdash; should be made selectable.
+ * </refsect2>
+ * <refsect2 id="label-text-layout">
+ * <title>Text layout</title>
+ * <para>
+ * A label can contain any number of paragraphs, but will have
+ * performance problems if it contains more than a small number.
+ * Paragraphs are separated by newlines or other paragraph separators
+ * understood by Pango.
+ *
+ * Labels can automatically wrap text if you call
+ * gtk_label_set_line_wrap().
+ *
+ * gtk_label_set_justify() sets how the lines in a label align
+ * with one another. If you want to set how the label as a whole
+ * aligns in its available space, see gtk_misc_set_alignment().
+ *
+ * The #GtkLabel:width-chars and #GtkLabel:max-width-chars properties
+ * can be used to control the size allocation of ellipsized or wrapped
+ * labels. For ellipsizing labels, if either is specified (and less
+ * than the actual text size), it is used as the minimum width, and the actual
+ * text size is used as the natural width of the label. For wrapping labels,
+ * width-chars is used as the minimum width, if specified, and max-width-chars
+ * is used as the natural width. Even if max-width-chars specified, wrapping
+ * labels will be rewrapped to use all of the available width.
+ *
+ * <note><para>
+ * Note that the interpretation of #GtkLabel:width-chars and
+ * #GtkLabel:max-width-chars has changed a bit with the introduction of
+ * <link linkend="geometry-management">width-for-height geometry management.</link>
+ * </para></note>
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Links</title>
+ * <para>
+ * Since 2.18, GTK+ supports markup for clickable hyperlinks in addition
+ * to regular Pango markup. The markup for links is borrowed from HTML, using the
+ * <tag>a</tag> with href and title attributes. GTK+ renders links similar to the
+ * way they appear in web browsers, with colored, underlined text. The title
+ * attribute is displayed as a tooltip on the link. An example looks like this:
+ *
+ * <informalexample><programlisting>
+ * gtk_label_set_markup (label, "Go to the <a href="http://www.gtk.org" title="&lt;i&gt;Our&lt;/i&gt; website">GTK+ website</a> for more...");
+ * </programlisting></informalexample>
+ *
+ * It is possible to implement custom handling for links and their tooltips with
+ * the #GtkLabel::activate-link signal and the gtk_label_get_current_uri() function.
+ * </para>
+ * </refsect2>
+ */
+
+
+/*rint() is only available in GCC and/or C99*/
+#if (__STDC_VERSION__ < 199901L && !defined __GNUC__)
+double rint(double x)
+{
+       if (ceil(x+0.5) == floor(x+0.5))
+       {
+               int a = (int)ceil(x);
+               if (a%2 == 0)
+                       return ceil(x);
+               else
+                       return floor(x);
+       }
+       else
+               return floor(x+0.5);
+}
+#endif
+
+
+
+struct _GtkLabelPrivate
 {
   GtkLabelSelectionInfo *select_info;
   GtkWidget *mnemonic_widget;
@@ -63,30 +260,28 @@ struct _GtkLabelPriv
   PangoAttrList *effective_attrs;
   PangoLayout   *layout;
 
-  gboolean mnemonics_visible;
-
   gchar   *label;
   gchar   *text;
 
-  guint    jtype            : 2;
-  guint    wrap             : 1;
-  guint    use_underline    : 1;
-  guint    use_markup       : 1;
-  guint    ellipsize        : 3;
-  guint    single_line_mode : 1;
-  guint    have_transform   : 1;
-  guint    in_click         : 1;
-  guint    wrap_mode        : 3;
-  guint    pattern_set      : 1;
-  guint    track_links      : 1;
+  gdouble  angle;
+
+  guint     mnemonics_visible : 1;
+  guint    jtype              : 2;
+  guint    wrap               : 1;
+  guint    use_underline      : 1;
+  guint    use_markup         : 1;
+  guint    ellipsize          : 3;
+  guint    single_line_mode   : 1;
+  guint    have_transform     : 1;
+  guint    in_click           : 1;
+  guint    wrap_mode          : 3;
+  guint    pattern_set        : 1;
+  guint    track_links        : 1;
 
   guint    mnemonic_keyval;
 
-  gint     wrap_width;
   gint     width_chars;
   gint     max_width_chars;
-
-  gdouble  angle;
 };
 
 /* Notes about the handling of links:
@@ -198,18 +393,17 @@ static void gtk_label_get_property      (GObject          *object,
                                         guint             prop_id,
                                         GValue           *value,
                                         GParamSpec       *pspec);
-static void gtk_label_destroy           (GtkObject        *object);
 static void gtk_label_finalize          (GObject          *object);
+static void gtk_label_destroy           (GtkWidget        *widget);
 static void gtk_label_size_allocate     (GtkWidget        *widget,
                                          GtkAllocation    *allocation);
 static void gtk_label_state_changed     (GtkWidget        *widget,
                                          GtkStateType      state);
-static void gtk_label_style_set         (GtkWidget        *widget,
-                                        GtkStyle         *previous_style);
+static void gtk_label_style_updated     (GtkWidget        *widget);
 static void gtk_label_direction_changed (GtkWidget        *widget,
                                         GtkTextDirection  previous_dir);
-static gint gtk_label_expose            (GtkWidget        *widget,
-                                        GdkEventExpose   *event);
+static gint gtk_label_draw              (GtkWidget        *widget,
+                                         cairo_t          *cr);
 static gboolean gtk_label_focus         (GtkWidget         *widget,
                                          GtkDirectionType   direction);
 
@@ -266,12 +460,12 @@ static void gtk_label_ensure_select_info  (GtkLabel *label);
 static void gtk_label_clear_select_info   (GtkLabel *label);
 static void gtk_label_update_cursor       (GtkLabel *label);
 static void gtk_label_clear_layout        (GtkLabel *label);
-static void gtk_label_ensure_layout       (GtkLabel *label, gboolean guess_wrap_width);
-static void gtk_label_invalidate_wrap_width (GtkLabel *label);
+static void gtk_label_ensure_layout       (GtkLabel *label);
 static void gtk_label_select_region_index (GtkLabel *label,
                                            gint      anchor_index,
                                            gint      end_index);
 
+
 static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
                                             gboolean           group_cycling);
 static void     gtk_label_setup_mnemonic    (GtkLabel          *label,
@@ -331,30 +525,27 @@ static void          gtk_label_get_link_colors  (GtkWidget  *widget,
 static void          emit_activate_link         (GtkLabel     *label,
                                                  GtkLabelLink *link);
 
-static void               gtk_label_size_request_init     (GtkSizeRequestIface *iface);
-static GtkSizeRequestMode gtk_label_get_request_mode      (GtkSizeRequest      *widget);
-static void               gtk_label_get_width             (GtkSizeRequest      *widget,
-                                                          gint                *minimum_size,
-                                                          gint                *natural_size);
-static void               gtk_label_get_height            (GtkSizeRequest      *widget,
-                                                          gint                *minimum_size,
-                                                          gint                *natural_size);
-static void               gtk_label_get_width_for_height  (GtkSizeRequest      *widget,
-                                                          gint                 height,
-                                                          gint                *minimum_width,
-                                                          gint                *natural_width);
-static void               gtk_label_get_height_for_width  (GtkSizeRequest      *widget,
-                                                          gint                 width,
-                                                          gint                *minimum_height,
-                                                          gint                *natural_height);
+static GtkSizeRequestMode gtk_label_get_request_mode                (GtkWidget           *widget);
+static void               gtk_label_get_preferred_width             (GtkWidget           *widget,
+                                                                     gint                *minimum_size,
+                                                                     gint                *natural_size);
+static void               gtk_label_get_preferred_height            (GtkWidget           *widget,
+                                                                     gint                *minimum_size,
+                                                                     gint                *natural_size);
+static void               gtk_label_get_preferred_width_for_height  (GtkWidget           *widget,
+                                                                     gint                 height,
+                                                                     gint                *minimum_width,
+                                                                     gint                *natural_width);
+static void               gtk_label_get_preferred_height_for_width  (GtkWidget           *widget,
+                                                                     gint                 width,
+                                                                     gint                *minimum_height,
+                                                                     gint                *natural_height);
 
 static GtkBuildableIface *buildable_parent_iface = NULL;
 
 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-                                               gtk_label_buildable_interface_init)
-                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
-                                                gtk_label_size_request_init));
+                                               gtk_label_buildable_interface_init))
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -383,7 +574,6 @@ static void
 gtk_label_class_init (GtkLabelClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-  GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
   GtkBindingSet *binding_set;
 
@@ -391,14 +581,13 @@ gtk_label_class_init (GtkLabelClass *class)
   gobject_class->get_property = gtk_label_get_property;
   gobject_class->finalize = gtk_label_finalize;
 
-  object_class->destroy = gtk_label_destroy;
-
+  widget_class->destroy = gtk_label_destroy;
   widget_class->size_allocate = gtk_label_size_allocate;
   widget_class->state_changed = gtk_label_state_changed;
-  widget_class->style_set = gtk_label_style_set;
+  widget_class->style_updated = gtk_label_style_updated;
   widget_class->query_tooltip = gtk_label_query_tooltip;
   widget_class->direction_changed = gtk_label_direction_changed;
-  widget_class->expose_event = gtk_label_expose;
+  widget_class->draw = gtk_label_draw;
   widget_class->realize = gtk_label_realize;
   widget_class->unrealize = gtk_label_unrealize;
   widget_class->map = gtk_label_map;
@@ -414,6 +603,11 @@ gtk_label_class_init (GtkLabelClass *class)
   widget_class->grab_focus = gtk_label_grab_focus;
   widget_class->popup_menu = gtk_label_popup_menu;
   widget_class->focus = gtk_label_focus;
+  widget_class->get_request_mode = gtk_label_get_request_mode;
+  widget_class->get_preferred_width = gtk_label_get_preferred_width;
+  widget_class->get_preferred_height = gtk_label_get_preferred_height;
+  widget_class->get_preferred_width_for_height = gtk_label_get_preferred_width_for_height;
+  widget_class->get_preferred_height_for_width = gtk_label_get_preferred_height_for_width;
 
   class->move_cursor = gtk_label_move_cursor;
   class->copy_clipboard = gtk_label_copy_clipboard;
@@ -515,7 +709,7 @@ gtk_label_class_init (GtkLabelClass *class)
      */
     signals[ACTIVATE_CURRENT_LINK] =
       g_signal_new_class_handler ("activate-current-link",
-                                  G_TYPE_FROM_CLASS (object_class),
+                                  G_TYPE_FROM_CLASS (gobject_class),
                                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                                   G_CALLBACK (gtk_label_activate_current_link),
                                   NULL, NULL,
@@ -537,7 +731,7 @@ gtk_label_class_init (GtkLabelClass *class)
      */
     signals[ACTIVATE_LINK] =
       g_signal_new ("activate-link",
-                    G_TYPE_FROM_CLASS (object_class),
+                    G_TYPE_FROM_CLASS (gobject_class),
                     G_SIGNAL_RUN_LAST,
                     G_STRUCT_OFFSET (GtkLabelClass, activate_link),
                     _gtk_boolean_handled_accumulator, NULL,
@@ -628,7 +822,7 @@ gtk_label_class_init (GtkLabelClass *class)
                                                      P_("The mnemonic accelerator key for this label"),
                                                      0,
                                                      G_MAXUINT,
-                                                     GDK_VoidSymbol,
+                                                     GDK_KEY_VoidSymbol,
                                                      GTK_PARAM_READABLE));
   g_object_class_install_property (gobject_class,
                                    PROP_MNEMONIC_WIDGET,
@@ -791,122 +985,118 @@ gtk_label_class_init (GtkLabelClass *class)
   binding_set = gtk_binding_set_by_class (class);
 
   /* Moving the insertion point */
-  add_move_binding (binding_set, GDK_Right, 0,
+  add_move_binding (binding_set, GDK_KEY_Right, 0,
                    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
   
-  add_move_binding (binding_set, GDK_Left, 0,
+  add_move_binding (binding_set, GDK_KEY_Left, 0,
                    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
 
-  add_move_binding (binding_set, GDK_KP_Right, 0,
+  add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
                    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
   
-  add_move_binding (binding_set, GDK_KP_Left, 0,
+  add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
                    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
   
-  add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_f, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
   
-  add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_b, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
   
-  add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_WORDS, 1);
 
-  add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_WORDS, -1);
 
-  add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_WORDS, 1);
 
-  add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_WORDS, -1);
 
   /* select all */
-  gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
                                "move-cursor", 3,
                                G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
                                G_TYPE_INT, -1,
                                G_TYPE_BOOLEAN, FALSE);
 
-  gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
                                "move-cursor", 3,
                                G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
                                G_TYPE_INT, 1,
                                G_TYPE_BOOLEAN, TRUE);
 
-  gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
                                "move-cursor", 3,
                                G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
                                G_TYPE_INT, -1,
                                G_TYPE_BOOLEAN, FALSE);
 
-  gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
                                "move-cursor", 3,
                                G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
                                G_TYPE_INT, 1,
                                G_TYPE_BOOLEAN, TRUE);
 
   /* unselect all */
-  gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
                                "move-cursor", 3,
                                G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
                                G_TYPE_INT, 0,
                                G_TYPE_BOOLEAN, FALSE);
 
-  gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
                                "move-cursor", 3,
                                G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS,
                                G_TYPE_INT, 0,
                                G_TYPE_BOOLEAN, FALSE);
 
-  add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
+  add_move_binding (binding_set, GDK_KEY_f, GDK_MOD1_MASK,
                    GTK_MOVEMENT_WORDS, 1);
 
-  add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
+  add_move_binding (binding_set, GDK_KEY_b, GDK_MOD1_MASK,
                    GTK_MOVEMENT_WORDS, -1);
 
-  add_move_binding (binding_set, GDK_Home, 0,
+  add_move_binding (binding_set, GDK_KEY_Home, 0,
                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
 
-  add_move_binding (binding_set, GDK_End, 0,
+  add_move_binding (binding_set, GDK_KEY_End, 0,
                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
 
-  add_move_binding (binding_set, GDK_KP_Home, 0,
+  add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
 
-  add_move_binding (binding_set, GDK_KP_End, 0,
+  add_move_binding (binding_set, GDK_KEY_KP_End, 0,
                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
   
-  add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_BUFFER_ENDS, -1);
 
-  add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_BUFFER_ENDS, 1);
 
-  add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_BUFFER_ENDS, -1);
 
-  add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
+  add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
                    GTK_MOVEMENT_BUFFER_ENDS, 1);
 
   /* copy */
-  gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
                                "copy-clipboard", 0);
 
-  gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
                                "activate-current-link", 0);
-  gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
                                "activate-current-link", 0);
-  gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
                                "activate-current-link", 0);
 
-  gtk_settings_install_property (g_param_spec_boolean ("gtk-label-select-on-focus",
-                                                      P_("Select on focus"),
-                                                      P_("Whether to select the contents of a selectable label when it is focused"),
-                                                      TRUE,
-                                                      GTK_PARAM_READWRITE));
+  g_type_class_add_private (class, sizeof (GtkLabelPrivate));
 
-  g_type_class_add_private (class, sizeof (GtkLabelPriv));
+  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE);
 }
 
 static void 
@@ -980,7 +1170,7 @@ gtk_label_get_property (GObject     *object,
                        GParamSpec  *pspec)
 {
   GtkLabel *label = GTK_LABEL (object);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   switch (prop_id)
     {
@@ -1015,24 +1205,10 @@ gtk_label_get_property (GObject     *object,
       g_value_set_object (value, (GObject*) priv->mnemonic_widget);
       break;
     case PROP_CURSOR_POSITION:
-      if (priv->select_info && priv->select_info->selectable)
-       {
-         gint offset = g_utf8_pointer_to_offset (priv->text,
-                                                 priv->text + priv->select_info->selection_end);
-         g_value_set_int (value, offset);
-       }
-      else
-       g_value_set_int (value, 0);
+      g_value_set_int (value, _gtk_label_get_cursor_position (label));
       break;
     case PROP_SELECTION_BOUND:
-      if (priv->select_info && priv->select_info->selectable)
-       {
-         gint offset = g_utf8_pointer_to_offset (priv->text,
-                                                 priv->text + priv->select_info->selection_anchor);
-         g_value_set_int (value, offset);
-       }
-      else
-       g_value_set_int (value, 0);
+      g_value_set_int (value, _gtk_label_get_selection_bound (label));
       break;
     case PROP_ELLIPSIZE:
       g_value_set_enum (value, priv->ellipsize);
@@ -1061,18 +1237,17 @@ gtk_label_get_property (GObject     *object,
 static void
 gtk_label_init (GtkLabel *label)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   label->priv = G_TYPE_INSTANCE_GET_PRIVATE (label,
                                              GTK_TYPE_LABEL,
-                                             GtkLabelPriv);
+                                             GtkLabelPrivate);
   priv = label->priv;
 
   gtk_widget_set_has_window (GTK_WIDGET (label), FALSE);
 
   priv->width_chars = -1;
   priv->max_width_chars = -1;
-  priv->wrap_width = -1;
   priv->label = NULL;
 
   priv->jtype = GTK_JUSTIFY_LEFT;
@@ -1085,7 +1260,7 @@ gtk_label_init (GtkLabel *label)
   priv->pattern_set = FALSE;
   priv->track_links = TRUE;
 
-  priv->mnemonic_keyval = GDK_VoidSymbol;
+  priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
   priv->layout = NULL;
   priv->text = NULL;
   priv->attrs = NULL;
@@ -1161,8 +1336,16 @@ attribute_from_text (GtkBuilder   *builder,
        attribute = pango_attr_stretch_new (g_value_get_enum (&val));
       break;
     case PANGO_ATTR_UNDERLINE:
-      if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
-       attribute = pango_attr_underline_new (g_value_get_boolean (&val));
+      if (gtk_builder_value_from_string_type (builder, PANGO_TYPE_UNDERLINE, value, &val, NULL))
+       attribute = pango_attr_underline_new (g_value_get_enum (&val));
+      else
+        {
+          /* XXX: allow boolean for backwards compat, so ignore error */
+          /* Deprecate this somehow */
+          g_value_unset (&val);
+          if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
+            attribute = pango_attr_underline_new (g_value_get_boolean (&val));
+        }
       break;
     case PANGO_ATTR_STRIKETHROUGH:     
       if (gtk_builder_value_from_string_type (builder, G_TYPE_BOOLEAN, value, &val, error))
@@ -1346,11 +1529,12 @@ pango_start_element (GMarkupParseContext *context,
        }
 
       attr = attribute_from_text (data->builder, name, value, error);
-      attr->start_index = start_val;
-      attr->end_index   = end_val;
 
       if (attr)
        {
+          attr->start_index = start_val;
+          attr->end_index   = end_val;
+
          if (!data->attrs)
            data->attrs = pango_attr_list_new ();
 
@@ -1485,7 +1669,7 @@ gtk_label_mnemonic_activate (GtkWidget *widget,
                             gboolean   group_cycling)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *parent;
 
   if (priv->mnemonic_widget)
@@ -1520,14 +1704,14 @@ static void
 gtk_label_setup_mnemonic (GtkLabel *label,
                          guint     last_key)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *widget = GTK_WIDGET (label);
   GtkWidget *toplevel;
   GtkWidget *mnemonic_menu;
   
   mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu");
   
-  if (last_key != GDK_VoidSymbol)
+  if (last_key != GDK_KEY_VoidSymbol)
     {
       if (priv->mnemonic_window)
        {
@@ -1545,7 +1729,7 @@ gtk_label_setup_mnemonic (GtkLabel *label,
        }
     }
   
-  if (priv->mnemonic_keyval == GDK_VoidSymbol)
+  if (priv->mnemonic_keyval == GDK_KEY_VoidSymbol)
       goto done;
 
   connect_mnemonics_visible_notify (GTK_LABEL (widget));
@@ -1584,7 +1768,7 @@ gtk_label_hierarchy_changed (GtkWidget *widget,
                             GtkWidget *old_toplevel)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   gtk_label_setup_mnemonic (label, priv->mnemonic_keyval);
 }
@@ -1632,7 +1816,7 @@ mnemonics_visible_apply (GtkWidget *widget,
                          gboolean   mnemonics_visible)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   mnemonics_visible = mnemonics_visible != FALSE;
 
@@ -1717,7 +1901,7 @@ label_mnemonic_widget_weak_notify (gpointer      data,
                                   GObject      *where_the_object_was)
 {
   GtkLabel *label = data;
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   priv->mnemonic_widget = NULL;
   g_object_notify (G_OBJECT (label), "mnemonic-widget");
@@ -1747,7 +1931,7 @@ void
 gtk_label_set_mnemonic_widget (GtkLabel  *label,
                               GtkWidget *widget)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -1782,8 +1966,8 @@ gtk_label_set_mnemonic_widget (GtkLabel  *label,
  * Retrieves the target of the mnemonic (keyboard shortcut) of this
  * label. See gtk_label_set_mnemonic_widget().
  *
- * Return value: the target of the label's mnemonic, or %NULL if none
- *               has been set and the default algorithm will be used.
+ * Return value: (transfer none): the target of the label's mnemonic,
+ *     or %NULL if none has been set and the default algorithm will be used.
  **/
 GtkWidget *
 gtk_label_get_mnemonic_widget (GtkLabel *label)
@@ -1806,7 +1990,7 @@ gtk_label_get_mnemonic_widget (GtkLabel *label)
 guint
 gtk_label_get_mnemonic_keyval (GtkLabel *label)
 {
-  g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
+  g_return_val_if_fail (GTK_IS_LABEL (label), GDK_KEY_VoidSymbol);
 
   return label->priv->mnemonic_keyval;
 }
@@ -1815,7 +1999,7 @@ static void
 gtk_label_set_text_internal (GtkLabel *label,
                             gchar    *str)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   g_free (priv->text);
 
@@ -1828,7 +2012,7 @@ static void
 gtk_label_set_label_internal (GtkLabel *label,
                              gchar    *str)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   g_free (priv->label);
 
@@ -1841,7 +2025,7 @@ static void
 gtk_label_set_use_markup_internal (GtkLabel *label,
                                   gboolean  val)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   val = val != FALSE;
   if (priv->use_markup != val)
@@ -1856,7 +2040,7 @@ static void
 gtk_label_set_use_underline_internal (GtkLabel *label,
                                      gboolean val)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   val = val != FALSE;
   if (priv->use_underline != val)
@@ -1870,7 +2054,7 @@ gtk_label_set_use_underline_internal (GtkLabel *label,
 static void
 gtk_label_compose_effective_attrs (GtkLabel *label)
 {
-  GtkLabelPriv      *priv = label->priv;
+  GtkLabelPrivate      *priv = label->priv;
   PangoAttrIterator *iter;
   PangoAttribute    *attr;
   GSList            *iter_attrs, *l;
@@ -1905,7 +2089,7 @@ static void
 gtk_label_set_attributes_internal (GtkLabel      *label,
                                   PangoAttrList *attrs)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (attrs)
     pango_attr_list_ref (attrs);
@@ -1924,7 +2108,7 @@ gtk_label_set_attributes_internal (GtkLabel      *label,
 static void
 gtk_label_recalculate (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   guint keyval = priv->mnemonic_keyval;
 
   if (priv->use_markup)
@@ -1945,7 +2129,7 @@ gtk_label_recalculate (GtkLabel *label)
   gtk_label_compose_effective_attrs (label);
 
   if (!priv->use_underline)
-    priv->mnemonic_keyval = GDK_VoidSymbol;
+    priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
 
   if (keyval != priv->mnemonic_keyval)
     {
@@ -2025,7 +2209,8 @@ gtk_label_set_attributes (GtkLabel         *label,
  * effective attributes for the label, use
  * pango_layout_get_attribute (gtk_label_get_layout (label)).
  *
- * Return value: the attribute list, or %NULL if none was set.
+ * Return value: (transfer none): the attribute list, or %NULL
+ *     if none was set.
  **/
 PangoAttrList *
 gtk_label_get_attributes (GtkLabel *label)
@@ -2070,7 +2255,7 @@ gtk_label_set_label (GtkLabel    *label,
  * Return value: the text of the label widget. This string is
  *   owned by the widget and must not be modified or freed.
  **/
-G_CONST_RETURN gchar *
+const gchar *
 gtk_label_get_label (GtkLabel *label)
 {
   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
@@ -2095,7 +2280,7 @@ start_element_handler (GMarkupParseContext  *context,
                        gpointer              user_data,
                        GError              **error)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
   UriParserData *pdata = user_data;
 
   if (strcmp (element_name, "a") == 0)
@@ -2263,11 +2448,13 @@ gtk_label_get_link_colors (GtkWidget  *widget,
                            GdkColor  **link_color,
                            GdkColor  **visited_link_color)
 {
-  gtk_widget_ensure_style (widget);
-  gtk_widget_style_get (widget,
-                        "link-color", link_color,
-                        "visited-link-color", visited_link_color,
-                        NULL);
+  GtkStyleContext *context;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_get_style (context,
+                               "link-color", link_color,
+                               "visited-link-color", visited_link_color,
+                                NULL);
   if (!*link_color)
     *link_color = gdk_color_copy (&default_link_color);
   if (!*visited_link_color)
@@ -2347,7 +2534,7 @@ failed:
 static void
 gtk_label_ensure_has_tooltip (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GList *l;
   gboolean has_tooltip = FALSE;
 
@@ -2369,7 +2556,7 @@ gtk_label_set_markup_internal (GtkLabel    *label,
                                const gchar *str,
                                gboolean     with_uline)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gchar *text = NULL;
   GError *error = NULL;
   PangoAttrList *attrs = NULL;
@@ -2452,7 +2639,7 @@ gtk_label_set_markup_internal (GtkLabel    *label,
   if (accel_char != 0)
     priv->mnemonic_keyval = gdk_keyval_to_lower (gdk_unicode_to_keyval (accel_char));
   else
-    priv->mnemonic_keyval = GDK_VoidSymbol;
+    priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
 }
 
 /**
@@ -2467,7 +2654,7 @@ gtk_label_set_markup_internal (GtkLabel    *label,
  * g_markup_printf_escaped()<!-- -->:
  * |[
  * char *markup;
- *   
+ *
  * markup = g_markup_printf_escaped ("&lt;span style=\"italic\"&gt;&percnt;s&lt;/span&gt;", str);
  * gtk_label_set_markup (GTK_LABEL (label), markup);
  * g_free (markup);
@@ -2477,18 +2664,14 @@ void
 gtk_label_set_markup (GtkLabel    *label,
                       const gchar *str)
 {
-  GtkLabelPriv *priv;
-
   g_return_if_fail (GTK_IS_LABEL (label));
 
-  priv = label->priv;
-
   g_object_freeze_notify (G_OBJECT (label));
 
   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
   gtk_label_set_use_markup_internal (label, TRUE);
   gtk_label_set_use_underline_internal (label, FALSE);
-  
+
   gtk_label_recalculate (label);
 
   g_object_thaw_notify (G_OBJECT (label));
@@ -2497,32 +2680,30 @@ gtk_label_set_markup (GtkLabel    *label,
 /**
  * gtk_label_set_markup_with_mnemonic:
  * @label: a #GtkLabel
- * @str: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
- * 
- * Parses @str which is marked up with the <link linkend="PangoMarkupFormat">Pango text markup language</link>,
+ * @str: a markup string (see
+ *     <link linkend="PangoMarkupFormat">Pango markup format</link>)
+ *
+ * Parses @str which is marked up with the
+ * <link linkend="PangoMarkupFormat">Pango text markup language</link>,
  * setting the label's text and attribute list based on the parse results.
  * If characters in @str are preceded by an underscore, they are underlined
  * indicating that they represent a keyboard accelerator called a mnemonic.
  *
- * The mnemonic key can be used to activate another widget, chosen 
+ * The mnemonic key can be used to activate another widget, chosen
  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
- **/
+ */
 void
 gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
-                                   const gchar *str)
+                                    const gchar *str)
 {
-  GtkLabelPriv *priv;
-
   g_return_if_fail (GTK_IS_LABEL (label));
 
-  priv = label->priv;
-
   g_object_freeze_notify (G_OBJECT (label));
 
   gtk_label_set_label_internal (label, g_strdup (str ? str : ""));
   gtk_label_set_use_markup_internal (label, TRUE);
   gtk_label_set_use_underline_internal (label, TRUE);
-  
+
   gtk_label_recalculate (label);
 
   g_object_thaw_notify (G_OBJECT (label));
@@ -2539,7 +2720,7 @@ gtk_label_set_markup_with_mnemonic (GtkLabel    *label,
  * Return value: the text in the label widget. This is the internal
  *   string used by the label, and must not be modified.
  **/
-G_CONST_RETURN gchar *
+const gchar *
 gtk_label_get_text (GtkLabel *label)
 {
   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
@@ -2551,7 +2732,7 @@ static PangoAttrList *
 gtk_label_pattern_to_attrs (GtkLabel      *label,
                            const gchar   *pattern)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   const char *start;
   const char *p = priv->text;
   const char *q = pattern;
@@ -2593,7 +2774,7 @@ gtk_label_set_pattern_internal (GtkLabel    *label,
                                const gchar *pattern,
                                 gboolean     is_mnemonic)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   PangoAttrList *attrs;
   gboolean enable_mnemonics;
   gboolean auto_mnemonics;
@@ -2625,11 +2806,21 @@ gtk_label_set_pattern_internal (GtkLabel    *label,
   priv->effective_attrs = attrs;
 }
 
+/**
+ * gtk_label_set_pattern:
+ * @label: The #GtkLabel you want to set the pattern to.
+ * @pattern: The pattern as described above.
+ *
+ * The pattern of underlines you want under the existing text within the
+ * #GtkLabel widget.  For example if the current text of the label says
+ * "FooBarBaz" passing a pattern of "___   ___" will underline
+ * "Foo" and "Baz" but not "Bar".
+ */
 void
 gtk_label_set_pattern (GtkLabel           *label,
                       const gchar *pattern)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -2666,7 +2857,7 @@ void
 gtk_label_set_justify (GtkLabel        *label,
                       GtkJustification jtype)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
   g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
@@ -2715,7 +2906,7 @@ void
 gtk_label_set_ellipsize (GtkLabel          *label,
                         PangoEllipsizeMode mode)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
@@ -2765,7 +2956,7 @@ void
 gtk_label_set_width_chars (GtkLabel *label,
                           gint      n_chars)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -2775,7 +2966,6 @@ gtk_label_set_width_chars (GtkLabel *label,
     {
       priv->width_chars = n_chars;
       g_object_notify (G_OBJECT (label), "width-chars");
-      gtk_label_invalidate_wrap_width (label);
       gtk_widget_queue_resize (GTK_WIDGET (label));
     }
 }
@@ -2812,7 +3002,7 @@ void
 gtk_label_set_max_width_chars (GtkLabel *label,
                               gint      n_chars)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -2823,7 +3013,6 @@ gtk_label_set_max_width_chars (GtkLabel *label,
       priv->max_width_chars = n_chars;
 
       g_object_notify (G_OBJECT (label), "max-width-chars");
-      gtk_label_invalidate_wrap_width (label);
       gtk_widget_queue_resize (GTK_WIDGET (label));
     }
 }
@@ -2866,7 +3055,7 @@ void
 gtk_label_set_line_wrap (GtkLabel *label,
                         gboolean  wrap)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -2916,7 +3105,7 @@ void
 gtk_label_set_line_wrap_mode (GtkLabel *label,
                              PangoWrapMode wrap_mode)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -2950,20 +3139,20 @@ gtk_label_get_line_wrap_mode (GtkLabel *label)
 }
 
 static void
-gtk_label_destroy (GtkObject *object)
+gtk_label_destroy (GtkWidget *widget)
 {
-  GtkLabel *label = GTK_LABEL (object);
+  GtkLabel *label = GTK_LABEL (widget);
 
   gtk_label_set_mnemonic_widget (label, NULL);
 
-  GTK_OBJECT_CLASS (gtk_label_parent_class)->destroy (object);
+  GTK_WIDGET_CLASS (gtk_label_parent_class)->destroy (widget);
 }
 
 static void
 gtk_label_finalize (GObject *object)
 {
   GtkLabel *label = GTK_LABEL (object);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   g_free (priv->label);
   g_free (priv->text);
@@ -2986,7 +3175,7 @@ gtk_label_finalize (GObject *object)
 static void
 gtk_label_clear_layout (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->layout)
     {
@@ -2997,179 +3186,197 @@ gtk_label_clear_layout (GtkLabel *label)
     }
 }
 
+static PangoFontMetrics *
+get_font_metrics (PangoContext *context, GtkWidget *widget)
+{
+  GtkStyleContext *style_context;
+  const PangoFontDescription *font;
+  PangoFontMetrics *retval;
 
-static void
-get_label_width (GtkLabel *label,
-                gint     *minimum,
-                gint     *natural)
-{
-  GtkWidgetAuxInfo *aux_info;
-  GtkLabelPriv     *priv;
-  PangoLayout      *layout;
-  PangoContext     *context;
-  PangoFontMetrics *metrics;
-  PangoRectangle    rect;
-  gint              char_width, digit_width, char_pixels, text_width, ellipsize_chars, guess_width;
-
-  priv     = label->priv;
-  aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
-
-  layout  = pango_layout_copy (priv->layout);
-  context = pango_layout_get_context (layout);
-  metrics = pango_context_get_metrics (context,
-                                       gtk_widget_get_style (GTK_WIDGET (label))->font_desc,
-                                      pango_context_get_language (context));
-  
-  char_width = pango_font_metrics_get_approximate_char_width (metrics);
-  digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
-  char_pixels = MAX (char_width, digit_width);
-  pango_font_metrics_unref (metrics);
-      
-  /* Fetch the length of the complete unwrapped text */
-  pango_layout_set_width (layout, -1);
-  pango_layout_get_extents (layout, NULL, &rect);
-  text_width = rect.width;
+  style_context = gtk_widget_get_style_context (widget);
+  font = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
 
-  /* Fetch the width that was guessed by gtk_label_ensure_layout() */
-  pango_layout_get_extents (priv->layout, NULL, &rect);
-  guess_width = rect.width;
+  retval = pango_context_get_metrics (context,
+                                      font,
+                                      pango_context_get_language (context));
 
-  /* enforce minimum width for ellipsized labels at ~3 chars */
-  if (priv->ellipsize)
-    ellipsize_chars = 3;
-  else
-    ellipsize_chars = 0;
+  return retval;
+}
 
-  /* "width-chars" Hard-coded minimum width: 
-   *    - minimum size should be MAX (width-chars, strlen ("..."));
-   *    - natural size should be MAX (width-chars, strlen (priv->text));
-   *
-   * "max-width-chars" User specified maximum size requisition
-   *    - minimum size should be MAX (width-chars, 0)
-   *    - natural size should be MIN (max-width-chars, strlen (priv->text))
-   *
-   *    For ellipsizing labels; if max-width-chars is specified: either it is used as 
-   *    a minimum size or the label text as a minimum size (natural size still overflows).
-   *
-   *    For wrapping labels; A reasonable minimum size is useful to naturally layout
-   *    interfaces automatically. In this case if no "width-chars" is specified, the minimum
-   *    width will default to the wrap guess that gtk_label_ensure_layout() does.
-   *
-   *    In *any* case the minimum width is completely overridden if an explicit width 
-   *    request was provided.
-   */
+/**
+ * gtk_label_get_measuring_layout:
+ * @label: the label
+ * @existing_layout: %NULL or an existing layout already in use.
+ * @width: the width to measure with in pango units, or -1 for infinite
+ * @height: the height to measure with in pango units, or -1 for infinite
+ *
+ * Gets a layout that can be used for measuring sizes. The returned
+ * layout will be identical to the label's layout except for the
+ * layout's width, which will be set to @width. Do not modify the returned
+ * layout.
+ *
+ * Returns: a new reference to a pango layout
+ **/
+static PangoLayout *
+gtk_label_get_measuring_layout (GtkLabel *   label,
+                                PangoLayout *existing_layout,
+                                int          width,
+                                int          height)
+{
+  GtkLabelPrivate *priv = label->priv;
+  PangoRectangle rect;
+  PangoLayout *copy;
 
-  if (priv->ellipsize || priv->wrap)
+  if (existing_layout != NULL)
     {
-      *minimum = char_pixels * MAX (priv->width_chars, ellipsize_chars);
-
-      /* Default to the minimum width regularly guessed by GTK+ if no minimum
-       * width was specified, only allow unwrapping of these labels.
-       *
-       * Note that when specifying a small width_chars for a long text;
-       * an accordingly large size will be required for the label height.
-       */
-      if (priv->wrap && priv->width_chars <= 0)
-       *minimum = guess_width;
+      if (existing_layout != priv->layout)
+        {
+          pango_layout_set_width (existing_layout, width);
+          pango_layout_set_height (existing_layout, height);
+          return existing_layout;
+        }
 
-      if (priv->max_width_chars < 0)
-       {
-         *natural = MAX (*minimum, text_width);
-       }
-      else
-       {
-         gint max_char_width = char_pixels * priv->max_width_chars;
-         gint max_width      = MIN (text_width, max_char_width);
+      g_object_unref (existing_layout);
+    }
 
-         /* With max-char-width specified, we let the minimum widths of 
-          * ellipsized text crawl up to the max-char-width
-          * (note that we dont want to limit the minimum width for wrapping text).
-          */
-         if (priv->ellipsize)
-           *minimum = MIN (text_width, max_width);
+  gtk_label_ensure_layout (label);
 
-         *natural = MAX (*minimum, max_width);
-       }
+  if (pango_layout_get_width (priv->layout) == width &&
+      pango_layout_get_height (priv->layout) == height)
+    {
+      g_object_ref (priv->layout);
+      return priv->layout;
     }
-  else
+
+  /* We can use the label's own layout if we're not allocated a size yet,
+   * because we don't need it to be properly setup at that point.
+   * This way we can make use of caching upon the label's creation.
+   */
+  if (gtk_widget_get_allocated_width (GTK_WIDGET (label)) <= 1)
     {
-      *minimum = text_width;
-      *natural = *minimum;
+      g_object_ref (priv->layout);
+      pango_layout_set_width (priv->layout, width);
+      pango_layout_set_height (priv->layout, height);
+      return priv->layout;
     }
 
-  /* if a width-request is set, use that as the requested label width */
-  if ((priv->wrap || priv->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0) &&
-      aux_info && aux_info->width > 0)
+  /* oftentimes we want to measure a width that is far wider than the current width,
+   * even though the layout would not change if we made it wider. In that case, we
+   * can just return the current layout, because for measuring purposes, it will be
+   * identical.
+   */
+  pango_layout_get_extents (priv->layout, NULL, &rect);
+  if ((width == -1 || rect.width <= width) &&
+      (height == -1 || rect.height <= height) &&
+      !pango_layout_is_wrapped (priv->layout) &&
+      !pango_layout_is_ellipsized (priv->layout))
     {
-      *minimum = aux_info->width * PANGO_SCALE;
-      *natural = MAX (*natural, *minimum);
+      g_object_ref (priv->layout);
+      return priv->layout;
     }
 
-  g_object_unref (layout);
+  copy = pango_layout_copy (priv->layout);
+  pango_layout_set_width (copy, width);
+  pango_layout_set_height (copy, height);
+  return copy;
 }
 
 static void
-gtk_label_invalidate_wrap_width (GtkLabel *label)
+gtk_label_update_layout_width (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
-
-  priv->wrap_width = -1;
-}
+  GtkLabelPrivate *priv = label->priv;
+  GtkWidget *widget = GTK_WIDGET (label);
 
-static gint
-get_label_wrap_width (GtkLabel *label)
-{
-  GtkLabelPriv *priv = label->priv;
+  g_assert (priv->layout);
 
-  if (priv->wrap_width < 0)
+  if (priv->ellipsize || priv->wrap)
     {
-      if (priv->width_chars > 0)
-       {
-         PangoLayout      *layout;
-         PangoContext     *context;
-         PangoFontMetrics *metrics;
-         PangoRectangle    rect;
-         gint              char_width, digit_width, char_pixels, text_width;
-
-         layout  = pango_layout_copy (priv->layout);
-         context = pango_layout_get_context (layout);
-          metrics = pango_context_get_metrics (context,
-                                               gtk_widget_get_style (GTK_WIDGET (label))->font_desc,
-                                              pango_context_get_language (context));
-         
-         char_width = pango_font_metrics_get_approximate_char_width (metrics);
-         digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
-         char_pixels = MAX (char_width, digit_width);
-         pango_font_metrics_unref (metrics);
-         
-         pango_layout_set_width (layout, -1);
-         pango_layout_get_extents (layout, NULL, &rect);
-         g_object_unref (layout);
+      PangoRectangle logical;
+      gint xpad, ypad;
+      gint width, height;
 
-         text_width = rect.width;
+      gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
 
-         priv->wrap_width = PANGO_PIXELS (MAX (text_width, char_pixels * priv->width_chars));
-       }
+      width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - xpad * 2;
+      height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - ypad * 2;
+
+      if (priv->have_transform)
+        {
+          PangoContext *context = gtk_widget_get_pango_context (widget);
+          const PangoMatrix *matrix = pango_context_get_matrix (context);
+          const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
+          const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
+
+          pango_layout_set_width (priv->layout, -1);
+          pango_layout_set_height (priv->layout, -1);
+          pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
+
+          if (fabs (dy) < 0.01)
+            {
+              if (logical.width > width)
+                pango_layout_set_width (priv->layout, width * PANGO_SCALE);
+            }
+          else if (fabs (dx) < 0.01)
+            {
+              if (logical.width > height)
+                pango_layout_set_width (priv->layout, height * PANGO_SCALE);
+            }
+          else
+            {
+              gdouble x0, y0, x1, y1, length;
+              gboolean vertical;
+              gint cy;
+
+              x0 = width / 2;
+              y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
+              vertical = fabs (y0) > height / 2;
+
+              if (vertical)
+                {
+                  y0 = height/2;
+                  x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
+                }
+
+              length = 2 * sqrt (x0 * x0 + y0 * y0);
+              pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
+              pango_layout_get_pixel_size (priv->layout, NULL, &cy);
+
+              x1 = +dy * cy/2;
+              y1 = -dx * cy/2;
+
+              if (vertical)
+                {
+                  y0 = height/2 + y1 - y0;
+                  x0 = -y0 * dx/dy;
+                }
+              else
+                {
+                  x0 = width/2 + x1 - x0;
+                  y0 = -x0 * dy/dx;
+                }
+
+              length = length - sqrt (x0 * x0 + y0 * y0) * 2;
+              pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
+            }
+        }
       else
-       {
-         PangoLayout *layout;
-  
-         layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), 
-                                                  "This string is just about long enough.");
-         pango_layout_get_size (layout, &priv->wrap_width, NULL);
-         g_object_unref (layout);
-       }
+        {
+          pango_layout_set_width (priv->layout, width * PANGO_SCALE);
+          pango_layout_set_height (priv->layout, priv->ellipsize ? height * PANGO_SCALE : -1);
+        }
+    }
+  else
+    {
+      pango_layout_set_width (priv->layout, -1);
+      pango_layout_set_height (priv->layout, -1);
     }
-
-  return priv->wrap_width;
 }
 
 static void
-gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
+gtk_label_ensure_layout (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *widget;
-  PangoRectangle logical_rect;
   gboolean rtl;
 
   widget = GTK_WIDGET (label);
@@ -3178,7 +3385,6 @@ gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
 
   if (!priv->layout)
     {
-      GtkAllocation allocation;
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
       gdouble angle = gtk_label_get_angle (label);
 
@@ -3232,105 +3438,10 @@ gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
 
       pango_layout_set_alignment (priv->layout, align);
       pango_layout_set_ellipsize (priv->layout, priv->ellipsize);
+      pango_layout_set_wrap (priv->layout, priv->wrap_mode);
       pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode);
 
-      gtk_widget_get_allocation (widget, &allocation);
-
-      if (priv->ellipsize)
-        pango_layout_set_width (priv->layout, allocation.width * PANGO_SCALE);
-      else if (priv->wrap)
-       {
-         GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
-         gint longest_paragraph;
-         gint width, height;
-         gint aux_width = 0;
-
-         if ((angle == 90 || angle == 270) && aux_info && aux_info->height > 0)
-           aux_width = aux_info->height;
-         else if (aux_info && aux_info->width > 0)
-           aux_width = aux_info->width;
-
-         if (aux_width > 0)
-           pango_layout_set_width (priv->layout, aux_width * PANGO_SCALE);
-         else if (guess_wrap_width == FALSE && allocation.width > 1 && allocation.height > 1)
-           {
-             PangoRectangle rect;
-             gint xpad, ypad, natural_width;
-              gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
-
-             if (angle == 90 || angle == 270)
-               width = allocation.height - ypad * 2;
-             else
-               width = allocation.width  - xpad * 2;
-
-             /* dont set a wrap width wider than the label's natural width
-              * incase we're allocated more space than needed */
-             pango_layout_get_extents (priv->layout, NULL, &rect);
-             natural_width = PANGO_PIXELS (rect.width);
-             width = MIN (natural_width, width);
-
-             pango_layout_set_wrap (priv->layout, priv->wrap_mode);
-             pango_layout_set_width (priv->layout, MAX (width, 1) * PANGO_SCALE);
-           }
-         else
-           {
-             GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
-             gint wrap_width;
-
-             pango_layout_set_width (priv->layout, -1);
-             pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-
-             width = logical_rect.width;
-             /* Try to guess a reasonable maximum width */
-             longest_paragraph = width;
-
-             wrap_width = get_label_wrap_width (label);
-             width = MIN (width, wrap_width);
-             width = MIN (width,
-                          PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
-
-             pango_layout_set_width (priv->layout, width);
-             pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-             width = logical_rect.width;
-             height = logical_rect.height;
-
-             /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
-              * so we try short search for a narrower width that leaves us with the same height
-              */
-             if (longest_paragraph > 0)
-               {
-                 gint nlines, perfect_width;
-
-                 nlines = pango_layout_get_line_count (priv->layout);
-                 perfect_width = (longest_paragraph + nlines - 1) / nlines;
-                 
-                 if (perfect_width < width)
-                   {
-                     pango_layout_set_width (priv->layout, perfect_width);
-                     pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-
-                     if (logical_rect.height <= height)
-                       width = logical_rect.width;
-                     else
-                       {
-                         gint mid_width = (perfect_width + width) / 2;
-                         
-                         if (mid_width > perfect_width)
-                           {
-                             pango_layout_set_width (priv->layout, mid_width);
-                             pango_layout_get_extents (priv->layout, NULL, &logical_rect);
-
-                             if (logical_rect.height <= height)
-                               width = logical_rect.width;
-                           }
-                       }
-                   }
-               }
-             pango_layout_set_width (priv->layout, width);
-           }
-       }
-      else /* !priv->wrap */
-       pango_layout_set_width (priv->layout, -1);
+      gtk_label_update_layout_width (label);
     }
 }
 
@@ -3343,9 +3454,7 @@ get_single_line_height (GtkWidget   *widget,
   gint ascent, descent;
 
   context = pango_layout_get_context (layout);
-  metrics = pango_context_get_metrics (context, gtk_widget_get_style (widget)->font_desc,
-                                       pango_context_get_language (context));
-
+  metrics = get_font_metrics (context, widget);
   ascent = pango_font_metrics_get_ascent (metrics);
   descent = pango_font_metrics_get_descent (metrics);
   pango_font_metrics_unref (metrics);
@@ -3353,26 +3462,18 @@ get_single_line_height (GtkWidget   *widget,
   return ascent + descent;
 }
 
-static void
-gtk_label_size_request_init (GtkSizeRequestIface *iface)
-{
-  iface->get_request_mode     = gtk_label_get_request_mode;
-  iface->get_width            = gtk_label_get_width;
-  iface->get_height           = gtk_label_get_height;
-  iface->get_width_for_height = gtk_label_get_width_for_height;
-  iface->get_height_for_width = gtk_label_get_height_for_width;
-}
-
 static GtkSizeRequestMode
-gtk_label_get_request_mode (GtkSizeRequest *layout)
+gtk_label_get_request_mode (GtkWidget *widget)
 {
-  GtkLabel *label = GTK_LABEL (layout);
+  GtkLabel *label = GTK_LABEL (widget);
   gdouble   angle = gtk_label_get_angle (label);
 
-  if (angle == 90 || angle == 270)
-    return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+  if (label->priv->wrap)
+    return (angle == 90 || angle == 270) ?
+      GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT : 
+      GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
 
-  return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+    return GTK_SIZE_REQUEST_CONSTANT_SIZE;
 }
 
 static void
@@ -3382,30 +3483,11 @@ get_size_for_allocation (GtkLabel        *label,
                          gint            *minimum_size,
                          gint            *natural_size)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   PangoLayout *layout;
-  GtkWidgetAuxInfo *aux_info =
-    _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
-  gint aux_size;
   gint text_height;
 
-  gtk_label_ensure_layout (label, FALSE);
-  layout = pango_layout_copy (priv->layout);
-
-  if (aux_info)
-    {
-      if (orientation == GTK_ORIENTATION_HORIZONTAL)
-        aux_size = aux_info->width;
-      else
-        aux_size = aux_info->height;
-    }
-  else
-    aux_size = 0;
-
-  if (aux_size > 0)
-    pango_layout_set_width (layout, aux_size * PANGO_SCALE);
-  else
-    pango_layout_set_width (layout, allocation * PANGO_SCALE);
+  layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE, -1);
 
   pango_layout_get_pixel_size (layout, NULL, &text_height);
 
@@ -3413,22 +3495,43 @@ get_size_for_allocation (GtkLabel        *label,
     *minimum_size = text_height;
 
   if (natural_size)
-    *natural_size = text_height;
+    {
+      if (priv->ellipsize && priv->wrap)
+        {
+          layout = gtk_label_get_measuring_layout (label, layout, allocation * PANGO_SCALE, G_MAXINT);
+          pango_layout_get_pixel_size (layout, NULL, &text_height);
+        }
+
+      *natural_size = text_height;
+    }
 
   g_object_unref (layout);
 }
 
+static gint
+get_char_pixels (GtkWidget   *label,
+                 PangoLayout *layout)
+{
+  PangoContext *context;
+  PangoFontMetrics *metrics;
+  gint char_width, digit_width;
+
+  context = pango_layout_get_context (layout);
+  metrics = get_font_metrics (context, GTK_WIDGET (label));
+  char_width = pango_font_metrics_get_approximate_char_width (metrics);
+  digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
+  pango_font_metrics_unref (metrics);
+
+  return MAX (char_width, digit_width);;
+}
+
 static void
-gtk_label_get_size (GtkSizeRequest *widget,
-                   GtkOrientation  orientation,
-                   gint           *minimum_size,
-                   gint           *natural_size)
+gtk_label_get_preferred_layout_size (GtkLabel *label,
+                                     PangoRectangle *required,
+                                     PangoRectangle *natural)
 {
-  GtkLabel      *label = GTK_LABEL (widget);
-  GtkLabelPriv  *priv = label->priv;
-  PangoRectangle required_rect;
-  PangoRectangle natural_rect;
-  gint           xpad, ypad;
+  GtkLabelPrivate *priv = label->priv;
+  PangoLayout *layout;
 
   /* "width-chars" Hard-coded minimum width:
    *    - minimum size should be MAX (width-chars, strlen ("..."));
@@ -3438,38 +3541,89 @@ gtk_label_get_size (GtkSizeRequest *widget,
    *    - minimum size should be MAX (width-chars, 0)
    *    - natural size should be MIN (max-width-chars, strlen (priv->text))
    *
+   *    For ellipsizing labels; if max-width-chars is specified: either it is used as 
+   *    a minimum size or the label text as a minimum size (natural size still overflows).
+   *
+   *    For wrapping labels; A reasonable minimum size is useful to naturally layout
+   *    interfaces automatically. In this case if no "width-chars" is specified, the minimum
+   *    width will default to the wrap guess that gtk_label_ensure_layout() does.
    */
 
-  /* When calculating ->wrap sometimes we need to invent a size; Ideally we should be doing
-   * that stuff here instead of inside gtk_label_ensure_layout() */
+  /* Start off with the pixel extents of an as-wide-as-possible layout */
+  layout = gtk_label_get_measuring_layout (label, NULL, -1, -1);
+
+  pango_layout_get_extents (layout, NULL, natural);
+  natural->x = natural->y = 0;
+
   if (priv->wrap)
-    gtk_label_clear_layout (label);
-  gtk_label_ensure_layout (label, TRUE);
+    natural->height = get_single_line_height (GTK_WIDGET (label), layout);
+
+  if (priv->ellipsize || priv->wrap)
+    {
+      /* a layout with width 0 will be as small as humanly possible */
+      layout = gtk_label_get_measuring_layout (label, layout, 0, -1);
+
+      pango_layout_get_extents (layout, NULL, required);
+
+      /* can happen when Pango decides to ellipsize text */
+      if (required->width > natural->width)
+        required->width = natural->width;
+
+      required->x = required->y = 0;
+      required->height = natural->height;
+    }
+  else
+    {
+      *required = *natural;
+    }
+
+  if (priv->width_chars > -1 || priv->max_width_chars > -1)
+    {
+      gint char_pixels;
 
-  /* Start off with the pixel extents of the rendered layout */
-  pango_layout_get_extents (priv->layout, NULL, &required_rect);
-  required_rect.x = required_rect.y = 0;
+      char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
+      
+      if (priv->width_chars > -1)
+        required->width = MAX (required->width, char_pixels * priv->width_chars);
 
-  if (priv->single_line_mode || priv->wrap)
-    required_rect.height = get_single_line_height (GTK_WIDGET (label), priv->layout);
+      if (priv->max_width_chars > -1)
+        natural->width = MIN (natural->width, priv->max_width_chars * char_pixels);
+      natural->width = MAX (natural->width, required->width);
+    }
 
-  natural_rect = required_rect;
+  g_object_unref (layout);
+}
+
+static void
+gtk_label_get_preferred_size (GtkWidget      *widget,
+                              GtkOrientation  orientation,
+                              gint           *minimum_size,
+                              gint           *natural_size)
+{
+  GtkLabel      *label = GTK_LABEL (widget);
+  GtkLabelPrivate  *priv = label->priv;
+  PangoRectangle required_rect;
+  PangoRectangle natural_rect;
+  gint           xpad, ypad;
 
-  /* Calculate text width itself based on GtkLabel property rules */
-  get_label_width (label, &required_rect.width, &natural_rect.width);
+  gtk_label_get_preferred_layout_size (label, &required_rect, &natural_rect);
 
   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
   if (priv->have_transform)
     {
-      PangoLayout       *layout  = pango_layout_copy (priv->layout);
-      PangoContext      *context = pango_layout_get_context (priv->layout);
-      const PangoMatrix *matrix  = pango_context_get_matrix (context);
+      PangoLayout *copy;
+      PangoContext *context;
+      const PangoMatrix *matrix;
 
-      pango_layout_set_width (layout, -1);
-      pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
+      copy = pango_layout_copy (priv->layout);
+      context = pango_layout_get_context (copy);
+      matrix = pango_context_get_matrix (context);
 
-      pango_layout_get_extents (layout, NULL, &natural_rect);
-      g_object_unref (layout);
+      pango_layout_set_width (copy, -1);
+      pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
+
+      pango_layout_get_extents (copy, NULL, &natural_rect);
+      g_object_unref (copy);
 
       pango_matrix_transform_rectangle (matrix, &required_rect);
       pango_matrix_transform_rectangle (matrix, &natural_rect);
@@ -3553,43 +3707,33 @@ gtk_label_get_size (GtkSizeRequest *widget,
       *minimum_size += ypad * 2;
       *natural_size += ypad * 2;
     }
-
-  /* Restore real allocated size of layout; sometimes size-requests
-   * are randomly called without a following allocation; for this case
-   * we need to make sure we dont have a mucked up layout because we
-   * went and guessed the wrap-size.
-   */
-  if (priv->wrap)
-    gtk_label_clear_layout (label);
-  gtk_label_ensure_layout (label, FALSE);
-
 }
 
 
 static void
-gtk_label_get_width (GtkSizeRequest *widget,
-                    gint           *minimum_size,
-                    gint           *natural_size)
+gtk_label_get_preferred_width (GtkWidget *widget,
+                               gint      *minimum_size,
+                               gint      *natural_size)
 {
-  gtk_label_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+  gtk_label_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
 }
 
 static void
-gtk_label_get_height (GtkSizeRequest *widget,
-                     gint           *minimum_size,
-                     gint           *natural_size)
+gtk_label_get_preferred_height (GtkWidget *widget,
+                                gint      *minimum_size,
+                                gint      *natural_size)
 {
-  gtk_label_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+  gtk_label_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
 }
 
 static void
-gtk_label_get_width_for_height (GtkSizeRequest *widget,
-                                gint            height,
-                                gint           *minimum_width,
-                                gint           *natural_width)
+gtk_label_get_preferred_width_for_height (GtkWidget *widget,
+                                          gint       height,
+                                          gint      *minimum_width,
+                                          gint      *natural_width)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
     {
@@ -3611,17 +3755,17 @@ gtk_label_get_width_for_height (GtkSizeRequest *widget,
         *natural_width += xpad * 2;
     }
   else
-    GTK_SIZE_REQUEST_GET_IFACE (widget)->get_width (widget, minimum_width, natural_width);
+    GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
 }
 
 static void
-gtk_label_get_height_for_width (GtkSizeRequest *widget,
-                                gint            width,
-                                gint           *minimum_height,
-                                gint           *natural_height)
+gtk_label_get_preferred_height_for_width (GtkWidget *widget,
+                                          gint       width,
+                                          gint      *minimum_height,
+                                          gint      *natural_height)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
     {
@@ -3643,7 +3787,7 @@ gtk_label_get_height_for_width (GtkSizeRequest *widget,
         *natural_height += ypad * 2;
     }
   else
-    GTK_SIZE_REQUEST_GET_IFACE (widget)->get_height (widget, minimum_height, natural_height);
+    GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
 }
 
 static void
@@ -3651,94 +3795,12 @@ gtk_label_size_allocate (GtkWidget     *widget,
                          GtkAllocation *allocation)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
 
-  /* The layout may have been recently cleared in get_size_for_orientation(),
-   * but the width at that point may not be the same as the allocated width
-   */
-  if (priv->wrap)
-    gtk_label_clear_layout (label);
-
-  gtk_label_ensure_layout (label, FALSE);
-
-  if (priv->ellipsize)
-    {
-      if (priv->layout)
-        {
-          PangoRectangle logical;
-          PangoRectangle bounds;
-          gint xpad, ypad;
-
-          gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
-
-          bounds.x = bounds.y = 0;
-          bounds.width = allocation->width - xpad * 2;
-          bounds.height = allocation->height - ypad * 2;
-
-          pango_layout_set_width (priv->layout, -1);
-          pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
-
-          if (priv->have_transform)
-            {
-              PangoContext *context = gtk_widget_get_pango_context (widget);
-              const PangoMatrix *matrix = pango_context_get_matrix (context);
-
-              const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
-              const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
-              if (fabs (dy) < 0.01)
-                {
-                  if (logical.width > bounds.width)
-                    pango_layout_set_width (priv->layout, bounds.width * PANGO_SCALE);
-                }
-              else if (fabs (dx) < 0.01)
-                {
-                  if (logical.width > bounds.height)
-                    pango_layout_set_width (priv->layout, bounds.height * PANGO_SCALE);
-                }
-              else
-                {
-                  gdouble x0, y0, x1, y1, length;
-                  gboolean vertical;
-                  gint cy;
-
-                  x0 = bounds.width / 2;
-                  y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
-                  vertical = fabs (y0) > bounds.height / 2;
-
-                  if (vertical)
-                    {
-                      y0 = bounds.height/2;
-                      x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
-                    }
-
-                  length = 2 * sqrt (x0 * x0 + y0 * y0);
-                  pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
-                  pango_layout_get_pixel_size (priv->layout, NULL, &cy);
-
-                  x1 = +dy * cy/2;
-                  y1 = -dx * cy/2;
-
-                  if (vertical)
-                    {
-                      y0 = bounds.height/2 + y1 - y0;
-                      x0 = -y0 * dx/dy;
-                    }
-                  else
-                    {
-                      x0 = bounds.width/2 + x1 - x0;
-                      y0 = -x0 * dy/dx;
-                    }
-                  length = length - sqrt (x0 * x0 + y0 * y0) * 2;
-                  pango_layout_set_width (priv->layout, rint (length * PANGO_SCALE));
-                }
-            }
-          else if (logical.width > bounds.width)
-            pango_layout_set_width (priv->layout, bounds.width * PANGO_SCALE);
-        }
-    }
+  if (priv->layout)
+    gtk_label_update_layout_width (label);
 
   if (priv->select_info && priv->select_info->window)
     {
@@ -3753,7 +3815,7 @@ gtk_label_size_allocate (GtkWidget     *widget,
 static void
 gtk_label_update_cursor (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *widget;
 
   if (!priv->select_info)
@@ -3783,7 +3845,7 @@ gtk_label_update_cursor (GtkLabel *label)
       gdk_window_set_cursor (priv->select_info->window, cursor);
 
       if (cursor)
-        gdk_cursor_unref (cursor);
+        g_object_unref (cursor);
     }
 }
 
@@ -3792,11 +3854,13 @@ gtk_label_state_changed (GtkWidget   *widget,
                          GtkStateType prev_state)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info)
     {
-      gtk_label_select_region (label, 0, 0);
+      if (!gtk_widget_is_sensitive (widget))
+        gtk_label_select_region (label, 0, 0);
+
       gtk_label_update_cursor (label);
     }
 
@@ -3805,14 +3869,14 @@ gtk_label_state_changed (GtkWidget   *widget,
 }
 
 static void
-gtk_label_style_set (GtkWidget *widget,
-                    GtkStyle  *previous_style)
+gtk_label_style_updated (GtkWidget *widget)
 {
   GtkLabel *label = GTK_LABEL (widget);
 
+  GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
+
   /* We have to clear the layout, fonts etc. may have changed */
   gtk_label_clear_layout (label);
-  gtk_label_invalidate_wrap_width (label);
 }
 
 static void 
@@ -3820,7 +3884,7 @@ gtk_label_direction_changed (GtkWidget        *widget,
                             GtkTextDirection previous_dir)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->layout)
     pango_layout_context_changed (priv->layout);
@@ -3836,7 +3900,7 @@ get_layout_location (GtkLabel  *label,
   GtkAllocation allocation;
   GtkMisc *misc;
   GtkWidget *widget;
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
   gint req_width, x, y;
   gint req_height;
   gint xpad, ypad;
@@ -3872,12 +3936,7 @@ get_layout_location (GtkLabel  *label,
 
   gtk_widget_get_allocation (widget, &allocation);
 
-  x = floor (allocation.x + xpad + xalign * (allocation.width - req_width));
-
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
-    x = MAX (x, allocation.x + xpad);
-  else
-    x = MIN (x, allocation.x + allocation.width - xpad);
+  x = floor (allocation.x + xpad + xalign * (allocation.width - req_width) - logical.x);
 
 
   /* bgo#315462 - For single-line labels, *do* align the requisition with
@@ -3894,9 +3953,9 @@ get_layout_location (GtkLabel  *label,
    *   middle".  You want to read the first line, at least, to get some context.
    */
   if (pango_layout_get_line_count (priv->layout) == 1)
-    y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign);
+    y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign) - logical.y;
   else
-    y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0));
+    y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
 
   if (xp)
     *xp = x;
@@ -3907,13 +3966,13 @@ get_layout_location (GtkLabel  *label,
 
 static void
 draw_insertion_cursor (GtkLabel      *label,
+                       cairo_t       *cr,
                       GdkRectangle  *cursor_location,
                       gboolean       is_primary,
                       PangoDirection direction,
                       gboolean       draw_arrow)
 {
   GtkWidget *widget = GTK_WIDGET (label);
-  GtkAllocation allocation;
   GtkTextDirection text_dir;
 
   if (direction == PANGO_DIRECTION_LTR)
@@ -3921,21 +3980,19 @@ draw_insertion_cursor (GtkLabel      *label,
   else
     text_dir = GTK_TEXT_DIR_RTL;
 
-  gtk_widget_get_allocation (widget, &allocation);
-  gtk_draw_insertion_cursor (widget, gtk_widget_get_window (widget),
-                             &allocation, cursor_location,
+  gtk_draw_insertion_cursor (widget, cr, cursor_location,
                             is_primary, text_dir, draw_arrow);
 }
 
 static PangoDirection
 get_cursor_direction (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GSList *l;
 
   g_assert (priv->select_info);
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label);
 
   for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
     {
@@ -3956,9 +4013,9 @@ get_cursor_direction (GtkLabel *label)
 }
 
 static void
-gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
+gtk_label_draw_cursor (GtkLabel  *label, cairo_t *cr, gint xoffset, gint yoffset)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *widget;
 
   if (priv->select_info == NULL)
@@ -3981,7 +4038,7 @@ gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
       cursor_direction = get_cursor_direction (label);
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label);
       
       pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
                                   &strong_pos, &weak_pos);
@@ -4016,7 +4073,7 @@ gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
       cursor_location.width = 0;
       cursor_location.height = PANGO_PIXELS (cursor1->height);
 
-      draw_insertion_cursor (label,
+      draw_insertion_cursor (label, cr,
                             &cursor_location, TRUE, dir1,
                             dir2 != PANGO_DIRECTION_NEUTRAL);
       
@@ -4027,7 +4084,7 @@ gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
          cursor_location.width = 0;
          cursor_location.height = PANGO_PIXELS (cursor2->height);
 
-         draw_insertion_cursor (label,
+         draw_insertion_cursor (label, cr,
                                 &cursor_location, FALSE, dir2,
                                 TRUE);
        }
@@ -4037,7 +4094,7 @@ gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
 static GtkLabelLink *
 gtk_label_get_focus_link (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
   GList *l;
 
@@ -4059,43 +4116,42 @@ gtk_label_get_focus_link (GtkLabel *label)
 }
 
 static gint
-gtk_label_expose (GtkWidget      *widget,
-                 GdkEventExpose *event)
+gtk_label_draw (GtkWidget *widget,
+                cairo_t   *cr)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
-  GtkStyle *style;
-  GdkWindow *window;
+  GtkAllocation allocation;
+  GtkStyleContext *context;
+  GtkStateFlags state;
   gint x, y;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label);
 
-  if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
-      priv->text && (*priv->text != '\0'))
+  if (priv->text && (*priv->text != '\0'))
     {
+      GdkRGBA bg_color, fg_color;
+
       get_layout_location (label, &x, &y);
 
-      style = gtk_widget_get_style (widget);
-      window = gtk_widget_get_window (widget);
+      context = gtk_widget_get_style_context (widget);
+      gtk_widget_get_allocation (widget, &allocation);
+
+      cairo_translate (cr, -allocation.x, -allocation.y);
 
-      gtk_paint_layout (style,
-                        window,
-                        gtk_widget_get_state (widget),
-                       FALSE,
-                        &event->area,
-                        widget,
-                        "label",
-                        x, y,
-                        priv->layout);
+      state = gtk_widget_get_state_flags (widget);
+      gtk_style_context_set_state (context, state);
+
+      gtk_render_layout (context, cr,
+                         x, y,
+                         priv->layout);
 
       if (info &&
           (info->selection_anchor != info->selection_end))
         {
           gint range[2];
           cairo_region_t *clip;
-         GtkStateType state;
-          cairo_t *cr;
 
           range[0] = info->selection_anchor;
           range[1] = info->selection_end;
@@ -4111,29 +4167,29 @@ gtk_label_expose (GtkWidget      *widget,
                                                    x, y,
                                                    range,
                                                    1);
-         cairo_region_intersect (clip, event->region);
 
-         /* FIXME should use gtk_paint, but it can't use a clip
-           * region
-           */
-
-          cr = gdk_cairo_create (event->window);
+         /* FIXME should use gtk_paint, but it can't use a clip region */
+          cairo_save (cr);
 
           gdk_cairo_region (cr, clip);
           cairo_clip (cr);
 
-         state = GTK_STATE_SELECTED;
-         if (!gtk_widget_has_focus (widget))
-           state = GTK_STATE_ACTIVE;
+          state = GTK_STATE_FLAG_SELECTED;
+
+          if (gtk_widget_has_focus (widget))
+            state |= GTK_STATE_FLAG_FOCUSED;
+
+          gtk_style_context_get_color (context, state, &fg_color);
+          gtk_style_context_get_background_color (context, state, &bg_color);
 
-          gdk_cairo_set_source_color (cr, &style->base[state]);
+          gdk_cairo_set_source_rgba (cr, &bg_color);
           cairo_paint (cr);
 
-          gdk_cairo_set_source_color (cr, &style->text[state]);
+          gdk_cairo_set_source_rgba (cr, &fg_color);
           cairo_move_to (cr, x, y);
           _gtk_pango_fill_layout (cr, priv->layout);
 
-          cairo_destroy (cr);
+          cairo_restore (cr);
           cairo_region_destroy (clip);
         }
       else if (info)
@@ -4144,12 +4200,11 @@ gtk_label_expose (GtkWidget      *widget,
           cairo_region_t *clip;
           GdkRectangle rect;
           GdkColor *text_color;
-          GdkColor *base_color;
           GdkColor *link_color;
           GdkColor *visited_link_color;
 
           if (info->selectable && gtk_widget_has_focus (widget))
-           gtk_label_draw_cursor (label, x, y);
+            gtk_label_draw_cursor (label, cr, x, y);
 
           focus_link = gtk_label_get_focus_link (label);
           active_link = info->active_link;
@@ -4157,15 +4212,12 @@ gtk_label_expose (GtkWidget      *widget,
 
           if (active_link)
             {
-              cairo_t *cr;
+              GdkRGBA bg_color;
 
               range[0] = active_link->start;
               range[1] = active_link->end;
 
-              cr = gdk_cairo_create (event->window);
-
-              gdk_cairo_region (cr, event->region);
-              cairo_clip (cr);
+              cairo_save (cr);
 
               clip = gdk_pango_layout_get_clip_region (priv->layout,
                                                        x, y,
@@ -4180,12 +4232,15 @@ gtk_label_expose (GtkWidget      *widget,
                 text_color = visited_link_color;
               else
                 text_color = link_color;
+
               if (info->link_clicked)
-                base_color = &style->base[GTK_STATE_ACTIVE];
+                state = GTK_STATE_FLAG_ACTIVE;
               else
-                base_color = &style->base[GTK_STATE_PRELIGHT];
+                state = GTK_STATE_FLAG_PRELIGHT;
+
+              gtk_style_context_get_background_color (context, state, &bg_color);
 
-              gdk_cairo_set_source_color (cr, base_color);
+              gdk_cairo_set_source_rgba (cr, &bg_color);
               cairo_paint (cr);
 
               gdk_cairo_set_source_color (cr, text_color);
@@ -4195,7 +4250,7 @@ gtk_label_expose (GtkWidget      *widget,
               gdk_color_free (link_color);
               gdk_color_free (visited_link_color);
 
-              cairo_destroy (cr);
+              cairo_restore (cr);
             }
 
           if (focus_link && gtk_widget_has_focus (widget))
@@ -4209,9 +4264,12 @@ gtk_label_expose (GtkWidget      *widget,
                                                        1);
               cairo_region_get_extents (clip, &rect);
 
-              gtk_paint_focus (style, window, gtk_widget_get_state (widget),
-                               &event->area, widget, "label",
-                               rect.x, rect.y, rect.width, rect.height);
+              state = gtk_widget_get_state_flags (widget);
+              gtk_style_context_set_state (context, state);
+
+              gtk_render_focus (context, cr,
+                                rect.x, rect.y,
+                                rect.width, rect.height);
 
               cairo_region_destroy (clip);
             }
@@ -4232,7 +4290,7 @@ separate_uline_pattern (const gchar  *str,
   gchar *dest;
   gchar *pattern_dest;
 
-  *accel_key = GDK_VoidSymbol;
+  *accel_key = GDK_KEY_VoidSymbol;
   *new_str = g_new (gchar, strlen (str) + 1);
   *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
 
@@ -4265,7 +4323,7 @@ separate_uline_pattern (const gchar  *str,
          else
            {
              *pattern_dest++ = '_';
-             if (*accel_key == GDK_VoidSymbol)
+             if (*accel_key == GDK_KEY_VoidSymbol)
                *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
            }
 
@@ -4301,8 +4359,8 @@ static void
 gtk_label_set_uline_text_internal (GtkLabel    *label,
                                   const gchar *str)
 {
-  GtkLabelPriv *priv = label->priv;
-  guint accel_key = GDK_VoidSymbol;
+  GtkLabelPrivate *priv = label->priv;
+  guint accel_key = GDK_KEY_VoidSymbol;
   gchar *new_str;
   gchar *pattern;
 
@@ -4355,7 +4413,7 @@ static void
 gtk_label_realize (GtkWidget *widget)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   GTK_WIDGET_CLASS (gtk_label_parent_class)->realize (widget);
 
@@ -4367,7 +4425,7 @@ static void
 gtk_label_unrealize (GtkWidget *widget)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info)
     gtk_label_destroy_window (label);
@@ -4379,7 +4437,7 @@ static void
 gtk_label_map (GtkWidget *widget)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   GTK_WIDGET_CLASS (gtk_label_parent_class)->map (widget);
 
@@ -4391,7 +4449,7 @@ static void
 gtk_label_unmap (GtkWidget *widget)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info)
     gdk_window_hide (priv->select_info->window);
@@ -4462,7 +4520,7 @@ get_layout_index (GtkLabel *label,
                   gint      y,
                   gint     *index)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint trailing = 0;
   const gchar *cluster;
   const gchar *cluster_end;
@@ -4470,7 +4528,7 @@ get_layout_index (GtkLabel *label,
 
   *index = 0;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label);
 
   window_to_layout_coords (label, &x, &y);
 
@@ -4497,7 +4555,7 @@ get_layout_index (GtkLabel *label,
 static void
 gtk_label_select_word (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint min, max;
 
   gint start_index = gtk_label_move_backward_word (label, priv->select_info->selection_end);
@@ -4518,7 +4576,7 @@ static void
 gtk_label_grab_focus (GtkWidget *widget)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gboolean select_on_focus;
   GtkLabelLink *link;
 
@@ -4553,7 +4611,7 @@ gtk_label_focus (GtkWidget        *widget,
                  GtkDirectionType  direction)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
   GtkLabelLink *focus_link;
   GList *l;
@@ -4663,7 +4721,7 @@ gtk_label_button_press (GtkWidget      *widget,
                         GdkEventButton *event)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
   gint index = 0;
   gint min, max;
@@ -4695,24 +4753,24 @@ gtk_label_button_press (GtkWidget      *widget,
   if (event->button == 1)
     {
       if (!gtk_widget_has_focus (widget))
-       {
-         priv->in_click = TRUE;
-         gtk_widget_grab_focus (widget);
-         priv->in_click = FALSE;
-       }
+        {
+          priv->in_click = TRUE;
+          gtk_widget_grab_focus (widget);
+          priv->in_click = FALSE;
+        }
 
       if (event->type == GDK_3BUTTON_PRESS)
-       {
-         gtk_label_select_region_index (label, 0, strlen (priv->text));
-         return TRUE;
-       }
+        {
+          gtk_label_select_region_index (label, 0, strlen (priv->text));
+          return TRUE;
+        }
 
       if (event->type == GDK_2BUTTON_PRESS)
-       {
+        {
           info->select_words = TRUE;
-         gtk_label_select_word (label);
-         return TRUE;
-       }
+          gtk_label_select_word (label);
+          return TRUE;
+        }
 
       get_layout_index (label, event->x, event->y, &index);
 
@@ -4720,38 +4778,49 @@ gtk_label_button_press (GtkWidget      *widget,
       max = MAX (info->selection_anchor, info->selection_end);
 
       if ((info->selection_anchor != info->selection_end) &&
-         (event->state & GDK_SHIFT_MASK))
-       {
-         /* extend (same as motion) */
-         min = MIN (min, index);
-         max = MAX (max, index);
+          (event->state & GDK_SHIFT_MASK))
+        {
+          if (index > min && index < max)
+            {
+              /* truncate selection, but keep it as big as possible */
+              if (index - min > max - index)
+                max = index;
+              else
+                min = index;
+            }
+          else
+            {
+              /* extend (same as motion) */
+              min = MIN (min, index);
+              max = MAX (max, index);
+            }
 
-         /* ensure the anchor is opposite index */
-         if (index == min)
-           {
-             gint tmp = min;
-             min = max;
-             max = tmp;
-           }
+          /* ensure the anchor is opposite index */
+          if (index == min)
+            {
+              gint tmp = min;
+              min = max;
+              max = tmp;
+            }
 
-         gtk_label_select_region_index (label, min, max);
-       }
+          gtk_label_select_region_index (label, min, max);
+        }
       else
-       {
-         if (event->type == GDK_3BUTTON_PRESS)
-           gtk_label_select_region_index (label, 0, strlen (priv->text));
-         else if (event->type == GDK_2BUTTON_PRESS)
-           gtk_label_select_word (label);
-         else if (min < max && min <= index && index <= max)
-           {
-             info->in_drag = TRUE;
-             info->drag_start_x = event->x;
-             info->drag_start_y = event->y;
-           }
-         else
-           /* start a replacement */
-           gtk_label_select_region_index (label, index, index);
-       }
+        {
+          if (event->type == GDK_3BUTTON_PRESS)
+            gtk_label_select_region_index (label, 0, strlen (priv->text));
+          else if (event->type == GDK_2BUTTON_PRESS)
+            gtk_label_select_word (label);
+          else if (min < max && min <= index && index <= max)
+            {
+              info->in_drag = TRUE;
+              info->drag_start_x = event->x;
+              info->drag_start_y = event->y;
+            }
+          else
+            /* start a replacement */
+            gtk_label_select_region_index (label, index, index);
+        }
 
       return TRUE;
     }
@@ -4770,7 +4839,7 @@ gtk_label_button_release (GtkWidget      *widget,
 
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
   gint index;
 
@@ -4809,7 +4878,7 @@ gtk_label_button_release (GtkWidget      *widget,
 static void
 connect_mnemonics_visible_notify (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *toplevel;
   gboolean connected;
 
@@ -4844,8 +4913,8 @@ drag_begin_cb (GtkWidget      *widget,
                gpointer        data)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
-  GdkPixmap *pixmap = NULL;
+  GtkLabelPrivate *priv = label->priv;
+  cairo_surface_t *surface = NULL;
 
   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
 
@@ -4865,26 +4934,24 @@ drag_begin_cb (GtkWidget      *widget,
 
       if (end > len)
         end = len;
-      
+
       if (start > len)
         start = len;
-      
-      pixmap = _gtk_text_util_create_drag_icon (widget, 
-                                               priv->text + start,
-                                               end - start);
+
+      surface = _gtk_text_util_create_drag_icon (widget,
+                                                 priv->text + start,
+                                                 end - start);
     }
 
-  if (pixmap)
-    gtk_drag_set_icon_pixmap (context,
-                              gdk_drawable_get_colormap (pixmap),
-                              pixmap,
-                              NULL,
-                              -2, -2);
+  if (surface)
+    {
+      gtk_drag_set_icon_surface (context, surface);
+      cairo_surface_destroy (surface);
+    }
   else
-    gtk_drag_set_icon_default (context);
-  
-  if (pixmap)
-    g_object_unref (pixmap);
+    {
+      gtk_drag_set_icon_default (context);
+    }
 }
 
 static gboolean
@@ -4892,7 +4959,7 @@ gtk_label_motion (GtkWidget      *widget,
                   GdkEventMotion *event)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
   gint index;
 
@@ -5028,7 +5095,7 @@ gtk_label_leave_notify (GtkWidget        *widget,
                         GdkEventCrossing *event)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info)
     {
@@ -5046,7 +5113,7 @@ gtk_label_leave_notify (GtkWidget        *widget,
 static void
 gtk_label_create_window (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkAllocation allocation;
   GtkWidget *widget;
   GdkWindowAttr attributes;
@@ -5089,13 +5156,13 @@ gtk_label_create_window (GtkLabel *label)
   gdk_window_set_user_data (priv->select_info->window, widget);
 
   if (attributes_mask & GDK_WA_CURSOR)
-    gdk_cursor_unref (attributes.cursor);
+    g_object_unref (attributes.cursor);
 }
 
 static void
 gtk_label_destroy_window (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   g_assert (priv->select_info);
 
@@ -5110,7 +5177,7 @@ gtk_label_destroy_window (GtkLabel *label)
 static void
 gtk_label_ensure_select_info (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info == NULL)
     {
@@ -5129,7 +5196,7 @@ gtk_label_ensure_select_info (GtkLabel *label)
 static void
 gtk_label_clear_select_info (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info == NULL)
     return;
@@ -5157,7 +5224,7 @@ void
 gtk_label_set_selectable (GtkLabel *label,
                           gboolean  setting)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
   gboolean old_setting;
 
   g_return_if_fail (GTK_IS_LABEL (label));
@@ -5207,7 +5274,7 @@ gtk_label_set_selectable (GtkLabel *label,
 gboolean
 gtk_label_get_selectable (GtkLabel *label)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
 
@@ -5233,7 +5300,7 @@ void
 gtk_label_set_angle (GtkLabel *label,
                     gdouble   angle)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -5280,7 +5347,7 @@ static void
 gtk_label_set_selection_text (GtkLabel         *label,
                              GtkSelectionData *selection_data)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if ((priv->select_info->selection_anchor !=
        priv->select_info->selection_end) &&
@@ -5332,7 +5399,7 @@ clear_text_callback (GtkClipboard     *clipboard,
                      gpointer          user_data_or_owner)
 {
   GtkLabel *label;
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   label = GTK_LABEL (user_data_or_owner);
   priv = label->priv;
@@ -5350,7 +5417,7 @@ gtk_label_select_region_index (GtkLabel *label,
                                gint      anchor_index,
                                gint      end_index)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -5361,14 +5428,24 @@ gtk_label_select_region_index (GtkLabel *label,
       GtkClipboard *clipboard;
 
       if (priv->select_info->selection_anchor == anchor_index &&
-         priv->select_info->selection_end == end_index)
-       return;
+          priv->select_info->selection_end == end_index)
+        return;
+
+      g_object_freeze_notify (G_OBJECT (label));
+
+      if (priv->select_info->selection_anchor != anchor_index)
+        g_object_notify (G_OBJECT (label), "selection-bound");
+      if (priv->select_info->selection_end != end_index)
+        g_object_notify (G_OBJECT (label), "cursor-position");
 
       priv->select_info->selection_anchor = anchor_index;
       priv->select_info->selection_end = end_index;
 
-      clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
-                                           GDK_SELECTION_PRIMARY);
+      if (gtk_widget_has_screen (GTK_WIDGET (label)))
+        clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
+                                              GDK_SELECTION_PRIMARY);
+      else
+        clipboard = NULL;
 
       if (anchor_index != end_index)
         {
@@ -5380,26 +5457,25 @@ gtk_label_select_region_index (GtkLabel *label,
           gtk_target_list_add_text_targets (list, 0);
           targets = gtk_target_table_new_from_list (list, &n_targets);
 
-          gtk_clipboard_set_with_owner (clipboard,
-                                        targets, n_targets,
-                                        get_text_callback,
-                                        clear_text_callback,
-                                        G_OBJECT (label));
+          if (clipboard)
+            gtk_clipboard_set_with_owner (clipboard,
+                                          targets, n_targets,
+                                          get_text_callback,
+                                          clear_text_callback,
+                                          G_OBJECT (label));
 
           gtk_target_table_free (targets, n_targets);
           gtk_target_list_unref (list);
         }
       else
         {
-          if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
+          if (clipboard &&
+              gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
             gtk_clipboard_clear (clipboard);
         }
 
       gtk_widget_queue_draw (GTK_WIDGET (label));
 
-      g_object_freeze_notify (G_OBJECT (label));
-      g_object_notify (G_OBJECT (label), "cursor-position");
-      g_object_notify (G_OBJECT (label), "selection-bound");
       g_object_thaw_notify (G_OBJECT (label));
     }
 }
@@ -5420,7 +5496,7 @@ gtk_label_select_region  (GtkLabel *label,
                           gint      start_offset,
                           gint      end_offset)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -5443,8 +5519,8 @@ gtk_label_select_region  (GtkLabel *label,
 /**
  * gtk_label_get_selection_bounds:
  * @label: a #GtkLabel
- * @start: return location for start of selection, as a character offset
- * @end: return location for end of selection, as a character offset
+ * @start: (out): return location for start of selection, as a character offset
+ * @end: (out): return location for end of selection, as a character offset
  * 
  * Gets the selected range of characters in the label, returning %TRUE
  * if there's a selection.
@@ -5456,7 +5532,7 @@ gtk_label_get_selection_bounds (GtkLabel  *label,
                                 gint      *start,
                                 gint      *end)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
 
@@ -5519,21 +5595,22 @@ gtk_label_get_selection_bounds (GtkLabel  *label,
  * Gets the #PangoLayout used to display the label.
  * The layout is useful to e.g. convert text positions to
  * pixel positions, in combination with gtk_label_get_layout_offsets().
- * The returned layout is owned by the label so need not be
- * freed by the caller.
+ * The returned layout is owned by the @label so need not be
+ * freed by the caller. The @label is free to recreate its layout at
+ * any time, so it should be considered read-only.
  *
  * Return value: (transfer none): the #PangoLayout for this label
  **/
 PangoLayout*
 gtk_label_get_layout (GtkLabel *label)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
 
   priv = label->priv;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label);
 
   return priv->layout;
 }
@@ -5541,8 +5618,8 @@ gtk_label_get_layout (GtkLabel *label)
 /**
  * gtk_label_get_layout_offsets:
  * @label: a #GtkLabel
- * @x: (allow-none): location to store X offset of layout, or %NULL
- * @y: (allow-none): location to store Y offset of layout, or %NULL
+ * @x: (out) (allow-none): location to store X offset of layout, or %NULL
+ * @y: (out) (allow-none): location to store Y offset of layout, or %NULL
  *
  * Obtains the coordinates where the label will draw the #PangoLayout
  * representing the text in the label; useful to convert mouse events
@@ -5560,7 +5637,7 @@ gtk_label_get_layout_offsets (GtkLabel *label,
 {
   g_return_if_fail (GTK_IS_LABEL (label));
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label);
 
   get_layout_location (label, x, y);
 }
@@ -5580,8 +5657,12 @@ gtk_label_set_use_markup (GtkLabel *label,
 {
   g_return_if_fail (GTK_IS_LABEL (label));
 
+  g_object_freeze_notify (G_OBJECT (label));
+
   gtk_label_set_use_markup_internal (label, setting);
   gtk_label_recalculate (label);
+
+  g_object_thaw_notify (G_OBJECT (label));
 }
 
 /**
@@ -5616,8 +5697,12 @@ gtk_label_set_use_underline (GtkLabel *label,
 {
   g_return_if_fail (GTK_IS_LABEL (label));
 
+  g_object_freeze_notify (G_OBJECT (label));
+
   gtk_label_set_use_underline_internal (label, setting);
   gtk_label_recalculate (label);
+
+  g_object_thaw_notify (G_OBJECT (label));
 }
 
 /**
@@ -5651,7 +5736,7 @@ void
 gtk_label_set_single_line_mode (GtkLabel *label,
                                 gboolean single_line_mode)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -5699,7 +5784,7 @@ get_better_cursor (GtkLabel *label,
                   gint      *x,
                   gint      *y)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)));
   PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
   PangoDirection cursor_direction = get_cursor_direction (label);
@@ -5710,7 +5795,7 @@ get_better_cursor (GtkLabel *label,
                "gtk-split-cursor", &split_cursor,
                NULL);
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label);
   
   pango_layout_get_cursor_pos (priv->layout, index,
                               &strong_pos, &weak_pos);
@@ -5741,7 +5826,7 @@ gtk_label_move_logically (GtkLabel *label,
                          gint      start,
                          gint      count)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint offset = g_utf8_pointer_to_offset (priv->text,
                                          priv->text + start);
 
@@ -5751,7 +5836,7 @@ gtk_label_move_logically (GtkLabel *label,
       gint n_attrs;
       gint length;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label);
       
       length = g_utf8_strlen (priv->text, -1);
 
@@ -5785,7 +5870,7 @@ gtk_label_move_visually (GtkLabel *label,
                         gint      start,
                         gint      count)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint index;
 
   index = start;
@@ -5796,7 +5881,7 @@ gtk_label_move_visually (GtkLabel *label,
       gboolean split_cursor;
       gboolean strong;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label);
 
       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
                    "gtk-split-cursor", &split_cursor,
@@ -5839,7 +5924,7 @@ static gint
 gtk_label_move_forward_word (GtkLabel *label,
                             gint      start)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint new_pos = g_utf8_pointer_to_offset (priv->text,
                                           priv->text + start);
   gint length;
@@ -5850,7 +5935,7 @@ gtk_label_move_forward_word (GtkLabel *label,
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label);
 
       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
 
@@ -5870,7 +5955,7 @@ static gint
 gtk_label_move_backward_word (GtkLabel *label,
                              gint      start)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint new_pos = g_utf8_pointer_to_offset (priv->text,
                                           priv->text + start);
 
@@ -5879,7 +5964,7 @@ gtk_label_move_backward_word (GtkLabel *label,
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label);
 
       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
 
@@ -5897,11 +5982,11 @@ gtk_label_move_backward_word (GtkLabel *label,
 
 static void
 gtk_label_move_cursor (GtkLabel       *label,
-                      GtkMovementStep step,
-                      gint            count,
-                      gboolean        extend_selection)
+                       GtkMovementStep step,
+                       gint            count,
+                       gboolean        extend_selection)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gint old_pos;
   gint new_pos;
 
@@ -5917,53 +6002,53 @@ gtk_label_move_cursor (GtkLabel       *label,
        * start/or end of the selection as appropriate
        */
       switch (step)
-       {
-       case GTK_MOVEMENT_VISUAL_POSITIONS:
-         {
-           gint end_x, end_y;
-           gint anchor_x, anchor_y;
-           gboolean end_is_left;
-
-           get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
-           get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
-
-           end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
-           
-           if (count < 0)
-             new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
-           else
-             new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
-           break;
-         }
-       case GTK_MOVEMENT_LOGICAL_POSITIONS:
-       case GTK_MOVEMENT_WORDS:
-         if (count < 0)
-           new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
-         else
-           new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
-       case GTK_MOVEMENT_PARAGRAPH_ENDS:
-       case GTK_MOVEMENT_BUFFER_ENDS:
-         /* FIXME: Can do better here */
-         new_pos = count < 0 ? 0 : strlen (priv->text);
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINES:
-       case GTK_MOVEMENT_PARAGRAPHS:
-       case GTK_MOVEMENT_PAGES:
-       case GTK_MOVEMENT_HORIZONTAL_PAGES:
-         break;
-       }
+        {
+        case GTK_MOVEMENT_VISUAL_POSITIONS:
+          {
+            gint end_x, end_y;
+            gint anchor_x, anchor_y;
+            gboolean end_is_left;
+
+            get_better_cursor (label, priv->select_info->selection_end, &end_x, &end_y);
+            get_better_cursor (label, priv->select_info->selection_anchor, &anchor_x, &anchor_y);
+
+            end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
+
+            if (count < 0)
+              new_pos = end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
+            else
+              new_pos = !end_is_left ? priv->select_info->selection_end : priv->select_info->selection_anchor;
+            break;
+          }
+        case GTK_MOVEMENT_LOGICAL_POSITIONS:
+        case GTK_MOVEMENT_WORDS:
+          if (count < 0)
+            new_pos = MIN (priv->select_info->selection_end, priv->select_info->selection_anchor);
+          else
+            new_pos = MAX (priv->select_info->selection_end, priv->select_info->selection_anchor);
+          break;
+        case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+        case GTK_MOVEMENT_PARAGRAPH_ENDS:
+        case GTK_MOVEMENT_BUFFER_ENDS:
+          /* FIXME: Can do better here */
+          new_pos = count < 0 ? 0 : strlen (priv->text);
+          break;
+        case GTK_MOVEMENT_DISPLAY_LINES:
+        case GTK_MOVEMENT_PARAGRAPHS:
+        case GTK_MOVEMENT_PAGES:
+        case GTK_MOVEMENT_HORIZONTAL_PAGES:
+          break;
+        }
     }
   else
     {
       switch (step)
-       {
-       case GTK_MOVEMENT_LOGICAL_POSITIONS:
-         new_pos = gtk_label_move_logically (label, new_pos, count);
-         break;
-       case GTK_MOVEMENT_VISUAL_POSITIONS:
-         new_pos = gtk_label_move_visually (label, new_pos, count);
+        {
+        case GTK_MOVEMENT_LOGICAL_POSITIONS:
+          new_pos = gtk_label_move_logically (label, new_pos, count);
+          break;
+        case GTK_MOVEMENT_VISUAL_POSITIONS:
+          new_pos = gtk_label_move_visually (label, new_pos, count);
           if (new_pos == old_pos)
             {
               if (!extend_selection)
@@ -5985,41 +6070,41 @@ gtk_label_move_cursor (GtkLabel       *label,
                   gtk_widget_error_bell (GTK_WIDGET (label));
                 }
             }
-         break;
-       case GTK_MOVEMENT_WORDS:
-         while (count > 0)
-           {
-             new_pos = gtk_label_move_forward_word (label, new_pos);
-             count--;
-           }
-         while (count < 0)
-           {
-             new_pos = gtk_label_move_backward_word (label, new_pos);
-             count++;
-           }
+          break;
+        case GTK_MOVEMENT_WORDS:
+          while (count > 0)
+            {
+              new_pos = gtk_label_move_forward_word (label, new_pos);
+              count--;
+            }
+          while (count < 0)
+            {
+              new_pos = gtk_label_move_backward_word (label, new_pos);
+              count++;
+            }
           if (new_pos == old_pos)
             gtk_widget_error_bell (GTK_WIDGET (label));
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
-       case GTK_MOVEMENT_PARAGRAPH_ENDS:
-       case GTK_MOVEMENT_BUFFER_ENDS:
-         /* FIXME: Can do better here */
-         new_pos = count < 0 ? 0 : strlen (priv->text);
+          break;
+        case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+        case GTK_MOVEMENT_PARAGRAPH_ENDS:
+        case GTK_MOVEMENT_BUFFER_ENDS:
+          /* FIXME: Can do better here */
+          new_pos = count < 0 ? 0 : strlen (priv->text);
           if (new_pos == old_pos)
             gtk_widget_error_bell (GTK_WIDGET (label));
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINES:
-       case GTK_MOVEMENT_PARAGRAPHS:
-       case GTK_MOVEMENT_PAGES:
-       case GTK_MOVEMENT_HORIZONTAL_PAGES:
-         break;
-       }
+          break;
+        case GTK_MOVEMENT_DISPLAY_LINES:
+        case GTK_MOVEMENT_PARAGRAPHS:
+        case GTK_MOVEMENT_PAGES:
+        case GTK_MOVEMENT_HORIZONTAL_PAGES:
+          break;
+        }
     }
 
   if (extend_selection)
     gtk_label_select_region_index (label,
-                                  priv->select_info->selection_anchor,
-                                  new_pos);
+                                   priv->select_info->selection_anchor,
+                                   new_pos);
   else
     gtk_label_select_region_index (label, new_pos, new_pos);
 }
@@ -6027,7 +6112,7 @@ gtk_label_move_cursor (GtkLabel       *label,
 static void
 gtk_label_copy_clipboard (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->text && priv->select_info)
     {
@@ -6066,7 +6151,7 @@ gtk_label_copy_clipboard (GtkLabel *label)
 static void
 gtk_label_select_all (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   gtk_label_select_region_index (label, 0, strlen (priv->text));
 }
@@ -6105,7 +6190,7 @@ popup_menu_detach (GtkWidget *attach_widget,
                   GtkMenu   *menu)
 {
   GtkLabel *label = GTK_LABEL (attach_widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (priv->select_info)
     priv->select_info->popup_menu = NULL;
@@ -6137,7 +6222,8 @@ popup_position_func (GtkMenu   *menu,
   *x += allocation.x;
   *y += allocation.y;
 
-  gtk_widget_size_request (GTK_WIDGET (menu), &req);
+  gtk_widget_get_preferred_size (GTK_WIDGET (menu),
+                                 &req, NULL);
 
   gtk_widget_get_allocation (widget, &allocation);
 
@@ -6187,7 +6273,7 @@ static void
 gtk_label_do_popup (GtkLabel       *label,
                     GdkEventButton *event)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkWidget *menuitem;
   GtkWidget *menu;
   GtkWidget *image;
@@ -6283,7 +6369,7 @@ gtk_label_do_popup (GtkLabel       *label,
 static void
 gtk_label_clear_links (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
 
   if (!priv->select_info)
     return;
@@ -6297,7 +6383,7 @@ gtk_label_clear_links (GtkLabel *label)
 static void
 gtk_label_rescan_links (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   PangoLayout *layout = priv->layout;
   PangoAttrList *attlist;
   PangoAttrIterator *iter;
@@ -6370,7 +6456,7 @@ static void
 emit_activate_link (GtkLabel     *label,
                     GtkLabelLink *link)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   gboolean handled;
 
   g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
@@ -6421,7 +6507,7 @@ gtk_label_activate_current_link (GtkLabel *label)
 static GtkLabelLink *
 gtk_label_get_current_link (GtkLabel *label)
 {
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelLink *link;
 
   if (!priv->select_info)
@@ -6452,7 +6538,7 @@ gtk_label_get_current_link (GtkLabel *label)
  *
  * Since: 2.18
  */
-G_CONST_RETURN gchar *
+const gchar *
 gtk_label_get_current_uri (GtkLabel *label)
 {
   GtkLabelLink *link;
@@ -6481,7 +6567,7 @@ void
 gtk_label_set_track_visited_links (GtkLabel *label,
                                    gboolean  track_links)
 {
-  GtkLabelPriv *priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -6527,7 +6613,7 @@ gtk_label_query_tooltip (GtkWidget  *widget,
                          GtkTooltip *tooltip)
 {
   GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPriv *priv = label->priv;
+  GtkLabelPrivate *priv = label->priv;
   GtkLabelSelectionInfo *info = priv->select_info;
   gint index = -1;
   GList *l;
@@ -6568,3 +6654,27 @@ gtk_label_query_tooltip (GtkWidget  *widget,
                                                                    keyboard_tip,
                                                                    tooltip);
 }
+
+gint
+_gtk_label_get_cursor_position (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = label->priv;
+
+  if (priv->select_info && priv->select_info->selectable)
+    return g_utf8_pointer_to_offset (priv->text,
+                                     priv->text + priv->select_info->selection_end);
+
+  return 0;
+}
+
+gint
+_gtk_label_get_selection_bound (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = label->priv;
+
+  if (priv->select_info && priv->select_info->selectable)
+    return g_utf8_pointer_to_offset (priv->text,
+                                     priv->text + priv->select_info->selection_anchor);
+
+  return 0;
+}