]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtklabel.c
label: refactor code
[~andy/gtk] / gtk / gtklabel.c
index ffe449748c00607ea90f59a0f39f198df9d6da9d..0eac38ddaf6f4e625b161b30280279c9dda60f58 100644 (file)
@@ -12,8 +12,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
  */
 
 /*
@@ -31,7 +30,6 @@
 #include "gtklabel.h"
 #include "gtkaccellabel.h"
 #include "gtkdnd.h"
-#include "gtkmainprivate.h"
 #include "gtkmarshalers.h"
 #include "gtkpango.h"
 #include "gtkwindow.h"
@@ -40,6 +38,7 @@
 #include "gtkintl.h"
 #include "gtkseparatormenuitem.h"
 #include "gtktextutil.h"
+#include "gtkmain.h"
 #include "gtkmenuitem.h"
 #include "gtkmenushellprivate.h"
 #include "gtknotebook.h"
 #include "gtktooltip.h"
 #include "gtkprivate.h"
 #include "gtktypebuiltins.h"
+#include "gtkmain.h"
 
-/*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
+#include "a11y/gtklabelaccessible.h"
 
+/* this is in case rint() is not provided by the compiler, 
+ * such as in the case of C89 compilers, like MSVC
+ */
+#include "fallback-c89.c"
 
+/**
+ * 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 #GtkComboBox.
+ *
+ * <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>
+ */
 
 struct _GtkLabelPrivate
 {
@@ -78,33 +242,31 @@ struct _GtkLabelPrivate
   GtkWindow *mnemonic_window;
 
   PangoAttrList *attrs;
-  PangoAttrList *effective_attrs;
+  PangoAttrList *markup_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:
@@ -220,11 +382,8 @@ 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_updated     (GtkWidget        *widget);
-static void gtk_label_direction_changed (GtkWidget        *widget,
-                                        GtkTextDirection  previous_dir);
+static void gtk_label_state_flags_changed   (GtkWidget        *widget,
+                                             GtkStateFlags     prev_state);
 static gint gtk_label_draw              (GtkWidget        *widget,
                                          cairo_t          *cr);
 static gboolean gtk_label_focus         (GtkWidget         *widget,
@@ -260,8 +419,6 @@ static void gtk_label_set_use_markup_internal    (GtkLabel      *label,
                                                  gboolean       val);
 static void gtk_label_set_use_underline_internal (GtkLabel      *label,
                                                  gboolean       val);
-static void gtk_label_set_attributes_internal    (GtkLabel      *label,
-                                                 PangoAttrList *attrs);
 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
                                                  const gchar   *str);
 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
@@ -284,11 +441,11 @@ 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);
-static void gtk_label_invalidate_wrap_width (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,
@@ -336,15 +493,14 @@ static gint gtk_label_move_backward_word (GtkLabel        *label,
                                          gint             start);
 
 /* For links: */
-static void          gtk_label_rescan_links     (GtkLabel  *label);
 static void          gtk_label_clear_links      (GtkLabel  *label);
 static gboolean      gtk_label_activate_link    (GtkLabel    *label,
                                                  const gchar *uri);
 static void          gtk_label_activate_current_link (GtkLabel *label);
 static GtkLabelLink *gtk_label_get_current_link (GtkLabel  *label);
 static void          gtk_label_get_link_colors  (GtkWidget  *widget,
-                                                 GdkColor  **link_color,
-                                                 GdkColor  **visited_link_color);
+                                                 GdkColor   *link_color,
+                                                 GdkColor   *visited_link_color);
 static void          emit_activate_link         (GtkLabel     *label,
                                                  GtkLabelLink *link);
 
@@ -406,10 +562,8 @@ gtk_label_class_init (GtkLabelClass *class)
 
   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_updated = gtk_label_style_updated;
+  widget_class->state_flags_changed = gtk_label_state_flags_changed;
   widget_class->query_tooltip = gtk_label_query_tooltip;
-  widget_class->direction_changed = gtk_label_direction_changed;
   widget_class->draw = gtk_label_draw;
   widget_class->realize = gtk_label_realize;
   widget_class->unrealize = gtk_label_unrealize;
@@ -681,7 +835,7 @@ gtk_label_class_init (GtkLabelClass *class)
    *
    * The preferred place to ellipsize the string, if the label does 
    * not have enough room to display the entire string, specified as a 
-   * #PangoEllisizeMode. 
+   * #PangoEllipsizeMode. 
    *
    * Note that setting this property to a value other than 
    * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests 
@@ -918,6 +1072,8 @@ gtk_label_class_init (GtkLabelClass *class)
                                "activate-current-link", 0);
 
   g_type_class_add_private (class, sizeof (GtkLabelPrivate));
+
+  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE);
 }
 
 static void 
@@ -1026,24 +1182,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);
@@ -1083,7 +1225,6 @@ gtk_label_init (GtkLabel *label)
 
   priv->width_chars = -1;
   priv->max_width_chars = -1;
-  priv->wrap_width = -1;
   priv->label = NULL;
 
   priv->jtype = GTK_JUSTIFY_LEFT;
@@ -1136,7 +1277,7 @@ attribute_from_text (GtkBuilder   *builder,
   PangoLanguage  *language;
   PangoFontDescription *font_desc;
   GdkColor       *color;
-  GValue          val = { 0, };
+  GValue          val = G_VALUE_INIT;
 
   if (!gtk_builder_value_from_string_type (builder, PANGO_TYPE_ATTR_TYPE, name, &val, error))
     return NULL;
@@ -1172,8 +1313,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))
@@ -1284,7 +1433,7 @@ pango_start_element (GMarkupParseContext *context,
                     GError             **error)
 {
   PangoParserData *data = (PangoParserData*)user_data;
-  GValue val = { 0, };
+  GValue val = G_VALUE_INIT;
   guint i;
   gint line_number, char_number;
 
@@ -1357,11 +1506,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 ();
 
@@ -1697,6 +1847,9 @@ gtk_label_screen_changed (GtkWidget *widget,
   GtkSettings *settings;
   gboolean shortcuts_connected;
 
+  /* The PangoContext is replaced when the screen changes, so clear the layouts */
+  gtk_label_clear_layout (GTK_LABEL (widget));
+
   if (!gtk_widget_has_screen (widget))
     return;
 
@@ -1810,9 +1963,9 @@ gtk_label_get_mnemonic_widget (GtkLabel *label)
  *
  * If the label has been set so that it has an mnemonic key this function
  * returns the keyval used for the mnemonic accelerator. If there is no
- * mnemonic set up it returns #GDK_VoidSymbol.
+ * mnemonic set up it returns #GDK_KEY_VoidSymbol.
  *
- * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
+ * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
  **/
 guint
 gtk_label_get_mnemonic_keyval (GtkLabel *label)
@@ -1824,12 +1977,14 @@ gtk_label_get_mnemonic_keyval (GtkLabel *label)
 
 static void
 gtk_label_set_text_internal (GtkLabel *label,
-                            gchar    *str)
+                             gchar    *str)
 {
   GtkLabelPrivate *priv = label->priv;
 
-  g_free (priv->text);
+  if (g_strcmp0 (priv->text, str) == 0)
+    return;
 
+  g_free (priv->text);
   priv->text = str;
 
   gtk_label_select_region_index (label, 0, 0);
@@ -1878,57 +2033,21 @@ gtk_label_set_use_underline_internal (GtkLabel *label,
     }
 }
 
-static void
-gtk_label_compose_effective_attrs (GtkLabel *label)
+static gboolean
+my_pango_attr_list_merge_filter (PangoAttribute *attribute,
+                                 gpointer        list)
 {
-  GtkLabelPrivate      *priv = label->priv;
-  PangoAttrIterator *iter;
-  PangoAttribute    *attr;
-  GSList            *iter_attrs, *l;
-
-  if (priv->attrs)
-    {
-      if (priv->effective_attrs)
-       {
-         if ((iter = pango_attr_list_get_iterator (priv->attrs)))
-           {
-             do
-               {
-                 iter_attrs = pango_attr_iterator_get_attrs (iter);
-                 for (l = iter_attrs; l; l = l->next)
-                   {
-                     attr = l->data;
-                     pango_attr_list_insert (priv->effective_attrs, attr);
-                   }
-                 g_slist_free (iter_attrs);
-               }
-             while (pango_attr_iterator_next (iter));
-             pango_attr_iterator_destroy (iter);
-           }
-       }
-      else
-       priv->effective_attrs =
-         pango_attr_list_ref (priv->attrs);
-    }
+  pango_attr_list_change (list, pango_attribute_copy (attribute));
+  return FALSE;
 }
 
 static void
-gtk_label_set_attributes_internal (GtkLabel      *label,
-                                  PangoAttrList *attrs)
+my_pango_attr_list_merge (PangoAttrList *into,
+                          PangoAttrList *from)
 {
-  GtkLabelPrivate *priv = label->priv;
-
-  if (attrs)
-    pango_attr_list_ref (attrs);
-
-  if (priv->attrs)
-    pango_attr_list_unref (priv->attrs);
-  priv->attrs = attrs;
-
-  g_object_notify (G_OBJECT (label), "attributes");
+  pango_attr_list_filter (from, my_pango_attr_list_merge_filter, into);
 }
 
-
 /* Calculates text, attrs and mnemonic_keyval from
  * label, use_underline and use_markup
  */
@@ -1938,23 +2057,23 @@ gtk_label_recalculate (GtkLabel *label)
   GtkLabelPrivate *priv = label->priv;
   guint keyval = priv->mnemonic_keyval;
 
+  gtk_label_clear_links (label);
+
   if (priv->use_markup)
     gtk_label_set_markup_internal (label, priv->label, priv->use_underline);
+  else if (priv->use_underline)
+    gtk_label_set_uline_text_internal (label, priv->label);
   else
     {
-      if (priv->use_underline)
-       gtk_label_set_uline_text_internal (label, priv->label);
-      else
+      if (!priv->pattern_set)
         {
-          if (priv->effective_attrs)
-            pango_attr_list_unref (priv->effective_attrs);
-          priv->effective_attrs = NULL;
-          gtk_label_set_text_internal (label, g_strdup (priv->label));
+          if (priv->markup_attrs)
+            pango_attr_list_unref (priv->markup_attrs);
+          priv->markup_attrs = NULL;
         }
+      gtk_label_set_text_internal (label, g_strdup (priv->label));
     }
 
-  gtk_label_compose_effective_attrs (label);
-
   if (!priv->use_underline)
     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
 
@@ -2015,11 +2134,18 @@ void
 gtk_label_set_attributes (GtkLabel         *label,
                           PangoAttrList    *attrs)
 {
+  GtkLabelPrivate *priv = label->priv;
+
   g_return_if_fail (GTK_IS_LABEL (label));
 
-  gtk_label_set_attributes_internal (label, attrs);
+  if (attrs)
+    pango_attr_list_ref (attrs);
+
+  if (priv->attrs)
+    pango_attr_list_unref (priv->attrs);
+  priv->attrs = attrs;
 
-  gtk_label_recalculate (label);
+  g_object_notify (G_OBJECT (label), "attributes");
 
   gtk_label_clear_layout (label);
   gtk_widget_queue_resize (GTK_WIDGET (label));
@@ -2082,7 +2208,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,8 +2221,7 @@ typedef struct
   GtkLabel *label;
   GList *links;
   GString *new_str;
-  GdkColor *link_color;
-  GdkColor *visited_link_color;
+  gsize text_len;
 } UriParserData;
 
 static void
@@ -2119,7 +2244,6 @@ start_element_handler (GMarkupParseContext  *context,
       gint line_number;
       gint char_number;
       gint i;
-      GdkColor *color = NULL;
 
       g_markup_parse_context_get_position (context, &line_number, &char_number);
 
@@ -2170,22 +2294,12 @@ start_element_handler (GMarkupParseContext  *context,
             }
         }
 
-      if (visited)
-        color = pdata->visited_link_color;
-      else
-        color = pdata->link_color;
-
-      g_string_append_printf (pdata->new_str,
-                              "<span color=\"#%04x%04x%04x\" underline=\"single\">",
-                              color->red,
-                              color->green,
-                              color->blue);
-
       link = g_new0 (GtkLabelLink, 1);
       link->uri = g_strdup (uri);
       link->title = g_strdup (title);
       link->visited = visited;
-      pdata->links = g_list_append (pdata->links, link);
+      link->start = pdata->text_len;
+      pdata->links = g_list_prepend (pdata->links, link);
     }
   else
     {
@@ -2223,7 +2337,10 @@ end_element_handler (GMarkupParseContext  *context,
   UriParserData *pdata = user_data;
 
   if (!strcmp (element_name, "a"))
-    g_string_append (pdata->new_str, "</span>");
+    {
+      GtkLabelLink *link = pdata->links->data;
+      link->end = pdata->text_len;
+    }
   else
     {
       g_string_append (pdata->new_str, "</");
@@ -2244,6 +2361,7 @@ text_handler (GMarkupParseContext  *context,
 
   newtext = g_markup_escape_text (text, text_len);
   g_string_append (pdata->new_str, newtext);
+  pdata->text_len += text_len;
   g_free (newtext);
 }
 
@@ -2271,21 +2389,33 @@ link_free (GtkLabelLink *link)
 }
 
 static void
-gtk_label_get_link_colors (GtkWidget  *widget,
-                           GdkColor  **link_color,
-                           GdkColor  **visited_link_color)
+gtk_label_get_link_colors (GtkWidget *widget,
+                           GdkColor  *link_color,
+                           GdkColor  *visited_link_color)
 {
   GtkStyleContext *context;
+  GdkColor *link, *visited;
 
   context = gtk_widget_get_style_context (widget);
   gtk_style_context_get_style (context,
-                               "link-color", link_color,
-                               "visited-link-color", visited_link_color,
+                               "link-color", &link,
+                               "visited-link-color", &visited,
                                 NULL);
-  if (!*link_color)
-    *link_color = gdk_color_copy (&default_link_color);
-  if (!*visited_link_color)
-    *visited_link_color = gdk_color_copy (&default_visited_link_color);
+  if (link)
+    {
+      *link_color = *link;
+      gdk_color_free (link);
+    }
+  else
+    *link_color = default_link_color;
+
+  if (visited)
+    {
+      *visited_link_color = *visited;
+      gdk_color_free (visited);
+    }
+  else
+    *visited_link_color = default_visited_link_color;
 }
 
 static gboolean
@@ -2308,8 +2438,7 @@ parse_uri_markup (GtkLabel     *label,
   pdata.label = label;
   pdata.links = NULL;
   pdata.new_str = g_string_sized_new (length);
-
-  gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
+  pdata.text_len = 0;
 
   while (p != end && xml_isspace (*p))
     p++;
@@ -2342,18 +2471,12 @@ parse_uri_markup (GtkLabel     *label,
   *new_str = g_string_free (pdata.new_str, FALSE);
   *links = pdata.links;
 
-  gdk_color_free (pdata.link_color);
-  gdk_color_free (pdata.visited_link_color);
-
   return TRUE;
 
 failed:
   g_markup_parse_context_free (context);
   g_string_free (pdata.new_str, TRUE);
-  g_list_foreach (pdata.links, (GFunc)link_free, NULL);
-  g_list_free (pdata.links);
-  gdk_color_free (pdata.link_color);
-  gdk_color_free (pdata.visited_link_color);
+  g_list_free_full (pdata.links, (GDestroyNotify) link_free);
 
   return FALSE;
 }
@@ -2399,11 +2522,10 @@ gtk_label_set_markup_internal (GtkLabel    *label,
       return;
     }
 
-  gtk_label_clear_links (label);
   if (links)
     {
       gtk_label_ensure_select_info (label);
-      priv->select_info->links = links;
+      priv->select_info->links = g_list_reverse (links);
       gtk_label_ensure_has_tooltip (label);
     }
 
@@ -2458,9 +2580,9 @@ gtk_label_set_markup_internal (GtkLabel    *label,
 
   if (attrs)
     {
-      if (priv->effective_attrs)
-       pango_attr_list_unref (priv->effective_attrs);
-      priv->effective_attrs = attrs;
+      if (priv->markup_attrs)
+       pango_attr_list_unref (priv->markup_attrs);
+      priv->markup_attrs = attrs;
     }
 
   if (accel_char != 0)
@@ -2547,7 +2669,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);
@@ -2628,16 +2750,26 @@ gtk_label_set_pattern_internal (GtkLabel    *label,
   else
     attrs = gtk_label_pattern_to_attrs (label, pattern);
 
-  if (priv->effective_attrs)
-    pango_attr_list_unref (priv->effective_attrs);
-  priv->effective_attrs = attrs;
+  if (priv->markup_attrs)
+    pango_attr_list_unref (priv->markup_attrs);
+  priv->markup_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)
 {
-  GtkLabelPrivate *priv = label->priv;
+  GtkLabelPrivate *priv;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
@@ -2783,7 +2915,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));
     }
 }
@@ -2831,7 +2962,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));
     }
 }
@@ -2982,8 +3112,8 @@ gtk_label_finalize (GObject *object)
   if (priv->attrs)
     pango_attr_list_unref (priv->attrs);
 
-  if (priv->effective_attrs)
-    pango_attr_list_unref (priv->effective_attrs);
+  if (priv->markup_attrs)
+    pango_attr_list_unref (priv->markup_attrs);
 
   gtk_label_clear_links (label);
   g_free (priv->select_info);
@@ -3000,31 +3130,9 @@ gtk_label_clear_layout (GtkLabel *label)
     {
       g_object_unref (priv->layout);
       priv->layout = NULL;
-
-      //gtk_label_clear_links (label);
     }
 }
 
-static PangoFontMetrics *
-get_font_metrics (PangoContext *context, GtkWidget *widget)
-{
-  GtkStyleContext *style_context;
-  PangoFontDescription *font;
-  PangoFontMetrics *retval;
-
-  style_context = gtk_widget_get_style_context (widget);
-  gtk_style_context_get (style_context, 0, "font", &font, NULL);
-
-  retval = pango_context_get_metrics (context,
-                                      font,
-                                      pango_context_get_language (context));
-
-  if (font != NULL)
-    pango_font_description_free (font);
-
-  return retval;
-}
-
 /**
  * gtk_label_get_measuring_layout:
  * @label: the label
@@ -3066,13 +3174,26 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
       return priv->layout;
     }
 
+  /* 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)
+    {
+      g_object_ref (priv->layout);
+      pango_layout_set_width (priv->layout, width);
+      return priv->layout;
+    }
+
   /* oftentimes we want to measure a width that is far wider than the current width,
-   * even though the layout is not wrapped. In that case, we can just return the
-   * current layout, because for measuring purposes, it will be identical.
+   * 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) &&
-      !pango_layout_is_wrapped (priv->layout))
+      !pango_layout_is_wrapped (priv->layout) &&
+      !pango_layout_is_ellipsized (priv->layout))
     {
       g_object_ref (priv->layout);
       return priv->layout;
@@ -3084,287 +3205,91 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
 }
 
 static void
-get_label_width (GtkLabel    *label,
-                 PangoLayout *guess_layout,
-                gint        *minimum,
-                gint        *natural)
+gtk_label_update_layout_width (GtkLabel *label)
 {
-  GtkLabelPrivate     *priv;
-  PangoLayout      *layout;
-  PangoRectangle    rect;
-  gint              text_width, ellipsize_chars, guess_width;
-
-  priv     = label->priv;
-
-  layout  = gtk_label_get_measuring_layout (label, NULL, -1);
-      
-  /* Fetch the length of the complete unwrapped text */
-  pango_layout_get_extents (layout, NULL, &rect);
-  text_width = rect.width;
-
-  /* Fetch the width that was guessed */
-  pango_layout_get_extents (guess_layout, NULL, &rect);
-  guess_width = rect.width;
-
-  /* enforce minimum width for ellipsized labels at ~3 chars */
-  if (priv->ellipsize)
-    ellipsize_chars = 3;
-  else
-    ellipsize_chars = 0;
+  GtkLabelPrivate *priv = label->priv;
+  GtkWidget *widget = GTK_WIDGET (label);
 
-  /* "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.
-   */
+  g_assert (priv->layout);
 
   if (priv->ellipsize || priv->wrap)
     {
-      PangoContext     *context;
-      PangoFontMetrics *metrics;
-      gint              char_width, digit_width, char_pixels;
-
-      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);
-      char_pixels = MAX (char_width, digit_width);
-      pango_font_metrics_unref (metrics);
-
-      *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 (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);
-
-         /* 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);
+      GtkBorder border;
+      PangoRectangle logical;
+      gint width, height;
 
-         *natural = MAX (*minimum, max_width);
-       }
-    }
-  else
-    {
-      *minimum = text_width;
-      *natural = *minimum;
-    }
+      _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
-  /* 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)
-    {
-      GtkWidgetAuxInfo *aux_info;
+      width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - border.left - border.right;
+      height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - border.top - border.bottom;
 
-      aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
-      if (aux_info && aux_info->width > 0)
+      if (priv->have_transform)
         {
-          *minimum = aux_info->width * PANGO_SCALE;
-          *natural = MAX (*natural, *minimum);
-        }
-    }
-
-  g_object_unref (layout);
-}
-
-static void
-gtk_label_invalidate_wrap_width (GtkLabel *label)
-{
-  GtkLabelPrivate *priv = label->priv;
-
-  priv->wrap_width = -1;
-}
-
-static gint
-get_label_wrap_width (GtkLabel *label)
-{
-  GtkLabelPrivate *priv = label->priv;
-
-  if (priv->wrap_width < 0)
-    {
-      if (priv->width_chars > 0)
-       {
-         PangoLayout      *layout;
-         PangoContext     *context;
-         PangoFontMetrics *metrics;
-         PangoRectangle    rect;
-         gint              char_width, digit_width, char_pixels, text_width;
-
-         layout  = gtk_label_get_measuring_layout (label, NULL, -1);
-         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);
-         char_pixels = MAX (char_width, digit_width);
-         pango_font_metrics_unref (metrics);
-         
-         pango_layout_get_extents (layout, NULL, &rect);
-         g_object_unref (layout);
-
-         text_width = rect.width;
-
-         priv->wrap_width = PANGO_PIXELS (MAX (text_width, char_pixels * priv->width_chars));
-       }
-      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);
-       }
-    }
-
-  return priv->wrap_width;
-}
-
-static PangoLayout *
-gtk_label_get_layout_with_guessed_wrap_width (GtkLabel *label)
-{
-  GdkScreen *screen;
-  PangoRectangle logical_rect;
-  gint wrap_width, width, height, longest_paragraph;
-  PangoLayout *layout;
-
-  if (!label->priv->wrap)
-    return gtk_label_get_measuring_layout (label, NULL, -1);
+          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) */
 
-  screen = gtk_widget_get_screen (GTK_WIDGET (label));
-
-  layout = gtk_label_get_measuring_layout (label, NULL, -1);
-  pango_layout_get_extents (layout, NULL, &logical_rect);
-
-  width = logical_rect.width;
-  /* Try to guess a reasonable maximum width */
-  longest_paragraph = width;
+          pango_layout_set_width (priv->layout, -1);
+          pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
 
-  wrap_width = get_label_wrap_width (label);
-  width = MIN (width, wrap_width);
-  width = MIN (width,
-               PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
+          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;
 
-  layout = gtk_label_get_measuring_layout (label, layout, width);
+              x0 = width / 2;
+              y0 = dx ? x0 * dy / dx : G_MAXDOUBLE;
+              vertical = fabs (y0) > height / 2;
 
-  pango_layout_get_extents (layout, NULL, &logical_rect);
-  width = logical_rect.width;
-  height = logical_rect.height;
+              if (vertical)
+                {
+                  y0 = height/2;
+                  x0 = dy ? y0 * dx / dy : G_MAXDOUBLE;
+                }
 
-  /* 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;
+              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);
 
-      nlines = pango_layout_get_line_count (layout);
-      perfect_width = (longest_paragraph + nlines - 1) / nlines;
-      
-      if (perfect_width < width)
-        {
-          layout = gtk_label_get_measuring_layout (label, layout, perfect_width);
-          pango_layout_get_extents (layout, NULL, &logical_rect);
+              x1 = +dy * cy/2;
+              y1 = -dx * cy/2;
 
-          if (logical_rect.height <= height)
-            width = logical_rect.width;
-          else
-            {
-              gint mid_width = (perfect_width + width) / 2;
-              
-              if (mid_width > perfect_width)
+              if (vertical)
                 {
-                  layout = gtk_label_get_measuring_layout (label, layout, mid_width);
-                  pango_layout_get_extents (layout, NULL, &logical_rect);
-
-                  if (logical_rect.height <= height)
-                    width = logical_rect.width;
+                  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));
             }
         }
-    }
-
-  return gtk_label_get_measuring_layout (label, layout, width);
-}
-
-static void
-gtk_label_update_layout_width (GtkLabel *label)
-{
-  GtkLabelPrivate *priv = label->priv;
-  GtkWidget *widget = GTK_WIDGET (label);
-  GtkAllocation allocation;
-
-  g_assert (priv->layout);
-
-  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);
-      gdouble angle = gtk_label_get_angle (label);
-      gint width;
-      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
         {
-          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);
+          pango_layout_set_width (priv->layout, width * PANGO_SCALE);
         }
     }
-  else /* !priv->wrap */
-    pango_layout_set_width (priv->layout, -1);
+  else
+    {
+      pango_layout_set_width (priv->layout, -1);
+    }
 }
 
 static void
@@ -3381,6 +3306,7 @@ gtk_label_ensure_layout (GtkLabel *label)
   if (!priv->layout)
     {
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+      PangoAttrList *attrs;
       gdouble angle = gtk_label_get_angle (label);
 
       if (angle != 0.0 && !priv->select_info)
@@ -3407,19 +3333,72 @@ gtk_label_ensure_layout (GtkLabel *label)
 
       priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
 
-      if (priv->effective_attrs)
-       pango_layout_set_attributes (priv->layout, priv->effective_attrs);
+      if (priv->select_info && priv->select_info->links)
+        {
+          GdkColor link_color, visited_color;
+          PangoAttribute *attribute;
+          GList *list;
+          
+          gtk_label_get_link_colors (widget, &link_color, &visited_color);
+          attrs = pango_attr_list_new ();
+
+          for (list = priv->select_info->links; list; list = list->next)
+            {
+              GtkLabelLink *link = list->data;
 
-      gtk_label_rescan_links (label);
+              attribute = pango_attr_underline_new (TRUE);
+              attribute->start_index = link->start;
+              attribute->end_index = link->end;
+              pango_attr_list_insert (attrs, attribute);
 
-      switch (priv->jtype)
-       {
-       case GTK_JUSTIFY_LEFT:
-         align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
-         break;
-       case GTK_JUSTIFY_RIGHT:
-         align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
-         break;
+              if (link->visited)
+                attribute = pango_attr_foreground_new (visited_color.red,
+                                                       visited_color.green,
+                                                       visited_color.blue);
+              else
+                attribute = pango_attr_foreground_new (link_color.red,
+                                                       link_color.green,
+                                                       link_color.blue);
+              attribute->start_index = link->start;
+              attribute->end_index = link->end;
+              pango_attr_list_insert (attrs, attribute);
+            }
+        }
+      else if (priv->markup_attrs || priv->attrs)
+        attrs = pango_attr_list_new ();
+      else
+        attrs = NULL;
+
+      if (priv->markup_attrs)
+        {
+          if (attrs)
+            my_pango_attr_list_merge (attrs, priv->markup_attrs);
+          else
+            attrs = pango_attr_list_ref (priv->markup_attrs);
+        }
+         
+      if (priv->attrs)
+        {
+          if (attrs)
+            my_pango_attr_list_merge (attrs, priv->attrs);
+          else
+            attrs = pango_attr_list_ref (priv->attrs);
+        }
+         
+      if (attrs)
+        {
+          pango_layout_set_attributes (priv->layout, attrs);
+          pango_attr_list_unref (attrs);
+        }
+
+      switch (priv->jtype)
+       {
+       case GTK_JUSTIFY_LEFT:
+         align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+         break;
+       case GTK_JUSTIFY_RIGHT:
+         align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
+         break;
        case GTK_JUSTIFY_CENTER:
          align = PANGO_ALIGN_CENTER;
          break;
@@ -3433,29 +3412,13 @@ gtk_label_ensure_layout (GtkLabel *label)
 
       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_label_update_layout_width (label);
     }
 }
 
-static gint
-get_single_line_height (GtkWidget   *widget,
-                        PangoLayout *layout)
-{
-  PangoContext *context;
-  PangoFontMetrics *metrics;
-  gint ascent, descent;
-
-  context = pango_layout_get_context (layout);
-  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);
-
-  return ascent + descent;
-}
-
 static GtkSizeRequestMode
 gtk_label_get_request_mode (GtkWidget *widget)
 {
@@ -3478,25 +3441,9 @@ get_size_for_allocation (GtkLabel        *label,
                          gint            *natural_size)
 {
   PangoLayout *layout;
-  GtkWidgetAuxInfo *aux_info =
-    _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
-  gint aux_size;
   gint text_height;
 
-  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)
-    layout = gtk_label_get_measuring_layout (label, NULL, aux_size * PANGO_SCALE);
-  else
-    layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE);
+  layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE);
 
   pango_layout_get_pixel_size (layout, NULL, &text_height);
 
@@ -3509,18 +3456,33 @@ get_size_for_allocation (GtkLabel        *label,
   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 = pango_context_get_metrics (context,
+                                       pango_context_get_font_description (context),
+                                       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);
+  pango_font_metrics_unref (metrics);
+
+  return MAX (char_width, digit_width);;
+}
+
 static void
-gtk_label_get_preferred_size (GtkWidget      *widget,
-                              GtkOrientation  orientation,
-                              gint           *minimum_size,
-                              gint           *natural_size)
+gtk_label_get_preferred_layout_size (GtkLabel *label,
+                                     PangoRectangle *smallest,
+                                     PangoRectangle *widest)
 {
-  GtkLabel      *label = GTK_LABEL (widget);
-  GtkLabelPrivate  *priv = label->priv;
-  PangoRectangle required_rect;
-  PangoRectangle natural_rect;
-  gint           xpad, ypad;
+  GtkLabelPrivate *priv = label->priv;
   PangoLayout *layout;
+  gint char_pixels;
 
   /* "width-chars" Hard-coded minimum width:
    *    - minimum size should be MAX (width-chars, strlen ("..."));
@@ -3530,48 +3492,89 @@ gtk_label_get_preferred_size (GtkWidget      *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() */
-  layout = gtk_label_get_layout_with_guessed_wrap_width (label);
+  /* Start off with the pixel extents of an as-wide-as-possible layout */
+  layout = gtk_label_get_measuring_layout (label, NULL, -1);
+
+  if (priv->width_chars > -1 || priv->max_width_chars > -1)
+    char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
+  else
+    char_pixels = 0;
+      
+  pango_layout_get_extents (layout, NULL, widest);
+  widest->width = MAX (widest->width, char_pixels * priv->width_chars);
+  widest->x = widest->y = 0;
+
+  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,
+                                               priv->width_chars > -1 ? char_pixels * priv->width_chars
+                                                                      : 0);
 
-  /* Start off with the pixel extents of the rendered layout */
-  pango_layout_get_extents (layout, NULL, &required_rect);
-  required_rect.x = required_rect.y = 0;
+      pango_layout_get_extents (layout, NULL, smallest);
+      smallest->width = MAX (smallest->width, char_pixels * priv->width_chars);
+      smallest->x = smallest->y = 0;
 
-  if (priv->single_line_mode || priv->wrap)
-    required_rect.height = get_single_line_height (GTK_WIDGET (label), layout);
+      if (priv->max_width_chars > -1 && widest->width > char_pixels * priv->max_width_chars)
+        {
+          layout = gtk_label_get_measuring_layout (label,
+                                                   layout,
+                                                   MAX (smallest->width, char_pixels * priv->max_width_chars));
+          pango_layout_get_extents (layout, NULL, widest);
+          widest->width = MAX (widest->width, char_pixels * priv->width_chars);
+          widest->x = widest->y = 0;
+        }
+    }
+  else
+    {
+      *smallest = *widest;
+    }
 
-  natural_rect = required_rect;
+  if (widest->width < smallest->width)
+    *smallest = *widest;
 
-  /* Calculate text width itself based on GtkLabel property rules */
-  get_label_width (label, layout, &required_rect.width, &natural_rect.width);
+  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 widest_rect;
+  PangoRectangle smallest_rect;
+  GtkBorder border;
+
+  gtk_label_get_preferred_layout_size (label, &smallest_rect, &widest_rect);
 
   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
   if (priv->have_transform)
     {
-      PangoLayout *copy;
       PangoContext *context;
       const PangoMatrix *matrix;
 
-      copy = pango_layout_copy (priv->layout);
-      context = pango_layout_get_context (copy);
+      context = pango_layout_get_context (priv->layout);
       matrix = pango_context_get_matrix (context);
 
-      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, &widest_rect);
+      pango_matrix_transform_rectangle (matrix, &smallest_rect);
 
-      pango_matrix_transform_rectangle (matrix, &required_rect);
-      pango_matrix_transform_rectangle (matrix, &natural_rect);
-
-      /* Bump the natural size in case of ellipsize to ensure pango has
+      /* Bump the size in case of ellipsize to ensure pango has
        * enough space in the angles (note, we could alternatively set the
        * layout to not ellipsize when we know we have been allocated our
-       * full natural size, or it may be that pango needs a fix here).
+       * full size, or it may be that pango needs a fix here).
        */
       if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 && 
           priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
@@ -3579,18 +3582,20 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
           /* For some reason we only need this at about 110 degrees, and only
            * when gaining in height
            */
-          natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
-          natural_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          widest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          widest_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          smallest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          smallest_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
         }
     }
 
-  required_rect.width  = PANGO_PIXELS_CEIL (required_rect.width);
-  required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+  widest_rect.width  = PANGO_PIXELS_CEIL (widest_rect.width);
+  widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
 
-  natural_rect.width  = PANGO_PIXELS_CEIL (natural_rect.width);
-  natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
+  smallest_rect.width  = PANGO_PIXELS_CEIL (smallest_rect.width);
+  smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
 
-  gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+  _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
   if (orientation == GTK_ORIENTATION_HORIZONTAL)
     {
@@ -3605,19 +3610,19 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
            */
           get_size_for_allocation (label,
                                    GTK_ORIENTATION_VERTICAL,
-                                   required_rect.height,
+                                   smallest_rect.height,
                                    minimum_size, natural_size);
 
         }
       else
         {
           /* Normal desired width */
-          *minimum_size = required_rect.width;
-          *natural_size = natural_rect.width;
+          *minimum_size = smallest_rect.width;
+          *natural_size = widest_rect.width;
         }
 
-      *minimum_size += xpad * 2;
-      *natural_size += xpad * 2;
+      *minimum_size += border.left + border.right;
+      *natural_size += border.left + border.right;
     }
   else /* GTK_ORIENTATION_VERTICAL */
     {
@@ -3632,7 +3637,7 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
            */
           get_size_for_allocation (label,
                                    GTK_ORIENTATION_HORIZONTAL,
-                                   required_rect.width,
+                                   widest_rect.width,
                                    minimum_size, natural_size);
         }
       else
@@ -3640,18 +3645,15 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
           /* A vertically rotated label does w4h, so return the base
            * desired height (text length)
            */
-          *minimum_size = required_rect.height;
-          *natural_size = natural_rect.height;
+          *minimum_size = MIN (smallest_rect.height, widest_rect.height);
+          *natural_size = MAX (smallest_rect.height, widest_rect.height);
         }
 
-      *minimum_size += ypad * 2;
-      *natural_size += ypad * 2;
+      *minimum_size += border.top + border.bottom;
+      *natural_size += border.top + border.bottom;
     }
-
-  g_object_unref (layout);
 }
 
-
 static void
 gtk_label_get_preferred_width (GtkWidget *widget,
                                gint      *minimum_size,
@@ -3679,22 +3681,22 @@ gtk_label_get_preferred_width_for_height (GtkWidget *widget,
 
   if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
     {
-      gint xpad, ypad;
+      GtkBorder border;
 
-      gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+      _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
       if (priv->wrap)
         gtk_label_clear_layout (label);
 
       get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
-                               MAX (1, height - (ypad * 2)),
+                               MAX (1, height - border.top - border.bottom),
                                minimum_width, natural_width);
 
       if (minimum_width)
-        *minimum_width += xpad * 2;
+        *minimum_width += border.right + border.left;
 
       if (natural_width)
-        *natural_width += xpad * 2;
+        *natural_width += border.right + border.left;
     }
   else
     GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
@@ -3711,22 +3713,22 @@ gtk_label_get_preferred_height_for_width (GtkWidget *widget,
 
   if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
     {
-      gint xpad, ypad;
+      GtkBorder border;
 
-      gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+      _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
       if (priv->wrap)
         gtk_label_clear_layout (label);
 
       get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
-                               MAX (1, width - xpad * 2),
+                               MAX (1, width - border.left - border.right),
                                minimum_height, natural_height);
 
       if (minimum_height)
-        *minimum_height += ypad * 2;
+        *minimum_height += border.top + border.bottom;
 
       if (natural_height)
-        *natural_height += ypad * 2;
+        *natural_height += border.top + border.bottom;
     }
   else
     GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
@@ -3741,90 +3743,8 @@ gtk_label_size_allocate (GtkWidget     *widget,
 
   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);
-
-  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)
     {
@@ -3874,8 +3794,8 @@ gtk_label_update_cursor (GtkLabel *label)
 }
 
 static void
-gtk_label_state_changed (GtkWidget   *widget,
-                         GtkStateType prev_state)
+gtk_label_state_flags_changed (GtkWidget     *widget,
+                               GtkStateFlags  prev_state)
 {
   GtkLabel *label = GTK_LABEL (widget);
   GtkLabelPrivate *priv = label->priv;
@@ -3888,33 +3808,8 @@ gtk_label_state_changed (GtkWidget   *widget,
       gtk_label_update_cursor (label);
     }
 
-  if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed)
-    GTK_WIDGET_CLASS (gtk_label_parent_class)->state_changed (widget, prev_state);
-}
-
-static void
-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 
-gtk_label_direction_changed (GtkWidget        *widget,
-                            GtkTextDirection previous_dir)
-{
-  GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPrivate *priv = label->priv;
-
-  if (priv->layout)
-    pango_layout_context_changed (priv->layout);
-
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
+  if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
+    GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
 }
 
 static void
@@ -3926,9 +3821,9 @@ get_layout_location (GtkLabel  *label,
   GtkMisc *misc;
   GtkWidget *widget;
   GtkLabelPrivate *priv;
+  GtkBorder border;
   gint req_width, x, y;
   gint req_height;
-  gint xpad, ypad;
   gfloat xalign, yalign;
   PangoRectangle logical;
 
@@ -3937,7 +3832,7 @@ get_layout_location (GtkLabel  *label,
   priv   = label->priv;
 
   gtk_misc_get_alignment (misc, &xalign, &yalign);
-  gtk_misc_get_padding (misc, &xpad, &ypad);
+  _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
   if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
     xalign = 1.0 - xalign;
@@ -3956,18 +3851,12 @@ get_layout_location (GtkLabel  *label,
   req_width  = logical.width;
   req_height = logical.height;
 
-  req_width  += 2 * xpad;
-  req_height += 2 * ypad;
+  req_width  += border.left + border.right;
+  req_height += border.top + border.bottom;
 
   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 + border.left + xalign * (allocation.width - req_width) - logical.x);
 
   /* bgo#315462 - For single-line labels, *do* align the requisition with
    * respect to the allocation, even if we are under-allocated.  For multi-line
@@ -3983,9 +3872,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 + border.top + (allocation.height - req_height) * yalign) - logical.y;
   else
-    y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0));
+    y = floor (allocation.y + border.top + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
 
   if (xp)
     *xp = x;
@@ -3994,26 +3883,6 @@ get_layout_location (GtkLabel  *label,
     *yp = y;
 }
 
-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);
-  GtkTextDirection text_dir;
-
-  if (direction == PANGO_DIRECTION_LTR)
-    text_dir = GTK_TEXT_DIR_LTR;
-  else
-    text_dir = GTK_TEXT_DIR_RTL;
-
-  gtk_draw_insertion_cursor (widget, cr, cursor_location,
-                            is_primary, text_dir, draw_arrow);
-}
-
 static PangoDirection
 get_cursor_direction (GtkLabel *label)
 {
@@ -4042,85 +3911,6 @@ get_cursor_direction (GtkLabel *label)
   return PANGO_DIRECTION_LTR;
 }
 
-static void
-gtk_label_draw_cursor (GtkLabel  *label, cairo_t *cr, gint xoffset, gint yoffset)
-{
-  GtkLabelPrivate *priv = label->priv;
-  GtkWidget *widget;
-
-  if (priv->select_info == NULL)
-    return;
-
-  widget = GTK_WIDGET (label);
-  
-  if (gtk_widget_is_drawable (widget))
-    {
-      PangoDirection keymap_direction;
-      PangoDirection cursor_direction;
-      PangoRectangle strong_pos, weak_pos;
-      gboolean split_cursor;
-      PangoRectangle *cursor1 = NULL;
-      PangoRectangle *cursor2 = NULL;
-      GdkRectangle cursor_location;
-      PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
-      PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
-
-      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);
-      
-      pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
-                                  &strong_pos, &weak_pos);
-
-      g_object_get (gtk_widget_get_settings (widget),
-                   "gtk-split-cursor", &split_cursor,
-                   NULL);
-
-      dir1 = cursor_direction;
-      
-      if (split_cursor)
-       {
-         cursor1 = &strong_pos;
-
-         if (strong_pos.x != weak_pos.x ||
-             strong_pos.y != weak_pos.y)
-           {
-             dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
-             cursor2 = &weak_pos;
-           }
-       }
-      else
-       {
-         if (keymap_direction == cursor_direction)
-           cursor1 = &strong_pos;
-         else
-           cursor1 = &weak_pos;
-       }
-      
-      cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
-      cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
-      cursor_location.width = 0;
-      cursor_location.height = PANGO_PIXELS (cursor1->height);
-
-      draw_insertion_cursor (label, cr,
-                            &cursor_location, TRUE, dir1,
-                            dir2 != PANGO_DIRECTION_NEUTRAL);
-      
-      if (dir2 != PANGO_DIRECTION_NEUTRAL)
-       {
-         cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
-         cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
-         cursor_location.width = 0;
-         cursor_location.height = PANGO_PIXELS (cursor2->height);
-
-         draw_insertion_cursor (label, cr,
-                                &cursor_location, FALSE, dir2,
-                                TRUE);
-       }
-    }
-}
-
 static GtkLabelLink *
 gtk_label_get_focus_link (GtkLabel *label)
 {
@@ -4159,29 +3949,34 @@ gtk_label_draw (GtkWidget *widget,
 
   gtk_label_ensure_layout (label);
 
+  context = gtk_widget_get_style_context (widget);
+  gtk_widget_get_allocation (widget, &allocation);
+
+  gtk_render_background (context, cr,
+                         0, 0,
+                         allocation.width, allocation.height);
+  gtk_render_frame (context, cr,
+                    0, 0,
+                    allocation.width, allocation.height);
+
   if (priv->text && (*priv->text != '\0'))
     {
-      GdkRGBA *bg_color, *fg_color;
-
       get_layout_location (label, &x, &y);
 
-      context = gtk_widget_get_style_context (widget);
-      gtk_widget_get_allocation (widget, &allocation);
-
       cairo_translate (cr, -allocation.x, -allocation.y);
 
-      state = gtk_widget_get_state_flags (widget);
-      gtk_style_context_set_state (context, state);
-
       gtk_render_layout (context, cr,
                          x, y,
                          priv->layout);
 
+      state = gtk_widget_get_state_flags (widget);
+
       if (info &&
           (info->selection_anchor != info->selection_end))
         {
           gint range[2];
           cairo_region_t *clip;
+          GdkRGBA bg_color, fg_color;
 
           range[0] = info->selection_anchor;
           range[1] = info->selection_end;
@@ -4204,28 +3999,20 @@ gtk_label_draw (GtkWidget *widget,
           gdk_cairo_region (cr, clip);
           cairo_clip (cr);
 
-          state = GTK_STATE_FLAG_SELECTED;
-
-          if (gtk_widget_has_focus (widget))
-            state |= GTK_STATE_FLAG_FOCUSED;
+          state |= GTK_STATE_FLAG_SELECTED;
 
-          gtk_style_context_get (context, state,
-                                 "background-color", &bg_color,
-                                 "color", &fg_color,
-                                 NULL);
+          gtk_style_context_get_color (context, state, &fg_color);
+          gtk_style_context_get_background_color (context, state, &bg_color);
 
-          gdk_cairo_set_source_rgba (cr, bg_color);
+          gdk_cairo_set_source_rgba (cr, &bg_color);
           cairo_paint (cr);
 
-          gdk_cairo_set_source_rgba (cr, fg_color);
+          gdk_cairo_set_source_rgba (cr, &fg_color);
           cairo_move_to (cr, x, y);
           _gtk_pango_fill_layout (cr, priv->layout);
 
           cairo_restore (cr);
           cairo_region_destroy (clip);
-
-          gdk_rgba_free (bg_color);
-          gdk_rgba_free (fg_color);
         }
       else if (info)
         {
@@ -4235,19 +4022,28 @@ gtk_label_draw (GtkWidget *widget,
           cairo_region_t *clip;
           GdkRectangle rect;
           GdkColor *text_color;
-          GdkColor *link_color;
-          GdkColor *visited_link_color;
+          GdkColor link_color;
+          GdkColor visited_link_color;
 
-          if (info->selectable && gtk_widget_has_focus (widget))
-            gtk_label_draw_cursor (label, cr, x, y);
+          if (info->selectable &&
+              gtk_widget_has_focus (widget) &&
+              gtk_widget_is_drawable (widget))
+            {
+              PangoDirection cursor_direction;
+
+              cursor_direction = get_cursor_direction (label);
+              gtk_render_insertion_cursor (context, cr,
+                                           x, y,
+                                           priv->layout, priv->select_info->selection_end,
+                                           cursor_direction);
+            }
 
           focus_link = gtk_label_get_focus_link (label);
           active_link = info->active_link;
 
-
           if (active_link)
             {
-              GdkRGBA *bg_color;
+              GdkRGBA bg_color;
 
               range[0] = active_link->start;
               range[1] = active_link->end;
@@ -4264,34 +4060,30 @@ gtk_label_draw (GtkWidget *widget,
 
               gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
               if (active_link->visited)
-                text_color = visited_link_color;
+                text_color = &visited_link_color;
               else
-                text_color = link_color;
+                text_color = &link_color;
 
               if (info->link_clicked)
-                state = GTK_STATE_FLAG_ACTIVE;
+                state |= GTK_STATE_FLAG_ACTIVE;
               else
-                state = GTK_STATE_FLAG_PRELIGHT;
+                state |= GTK_STATE_FLAG_PRELIGHT;
 
-              gtk_style_context_get (context, state,
-                                     "background-color", &bg_color,
-                                     NULL);
+              gtk_style_context_get_background_color (context, state, &bg_color);
 
-              gdk_cairo_set_source_rgba (cr, bg_color);
+              gdk_cairo_set_source_rgba (cr, &bg_color);
               cairo_paint (cr);
 
-              gdk_cairo_set_source_color (cr, text_color);
+              cairo_set_source_rgb (cr, text_color->red / 65535., 
+                                        text_color->green / 65535.,
+                                        text_color->blue / 65535.);
               cairo_move_to (cr, x, y);
               _gtk_pango_fill_layout (cr, priv->layout);
 
-              gdk_color_free (link_color);
-              gdk_color_free (visited_link_color);
-              gdk_rgba_free (bg_color);
-
               cairo_restore (cr);
             }
 
-          if (focus_link && gtk_widget_has_focus (widget))
+          if (focus_link && gtk_widget_has_visible_focus (widget))
             {
               range[0] = focus_link->start;
               range[1] = focus_link->end;
@@ -4302,9 +4094,6 @@ gtk_label_draw (GtkWidget *widget,
                                                        1);
               cairo_region_get_extents (clip, &rect);
 
-              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);
@@ -4769,16 +4558,16 @@ gtk_label_button_press (GtkWidget      *widget,
 
   if (info->active_link)
     {
-      if (event->button == 1)
+      if (gdk_event_triggers_context_menu ((GdkEvent *) event))
         {
           info->link_clicked = 1;
-          gtk_widget_queue_draw (widget);
+          gtk_label_do_popup (label, event);
+          return TRUE;
         }
-      else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+      else if (event->button == GDK_BUTTON_PRIMARY)
         {
           info->link_clicked = 1;
-          gtk_label_do_popup (label, event);
-          return TRUE;
+          gtk_widget_queue_draw (widget);
         }
     }
 
@@ -4788,27 +4577,33 @@ gtk_label_button_press (GtkWidget      *widget,
   info->in_drag = FALSE;
   info->select_words = FALSE;
 
-  if (event->button == 1)
+  if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+    {
+      gtk_label_do_popup (label, event);
+
+      return TRUE;
+    }
+  else if (event->button == GDK_BUTTON_PRIMARY)
     {
       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);
 
@@ -4816,47 +4611,53 @@ 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;
     }
-  else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
-    {
-      gtk_label_do_popup (label, event);
 
-      return TRUE;
-    }
   return FALSE;
 }
 
@@ -4883,7 +4684,7 @@ gtk_label_button_release (GtkWidget      *widget,
       return FALSE;
     }
 
-  if (event->button != 1)
+  if (event->button != GDK_BUTTON_PRIMARY)
     return FALSE;
 
   if (info->active_link &&
@@ -5180,7 +4981,7 @@ gtk_label_create_window (GtkLabel *label)
 
   priv->select_info->window = gdk_window_new (gtk_widget_get_window (widget),
                                                &attributes, attributes_mask);
-  gdk_window_set_user_data (priv->select_info->window, widget);
+  gtk_widget_register_window (widget, priv->select_info->window);
 
   if (attributes_mask & GDK_WA_CURSOR)
     g_object_unref (attributes.cursor);
@@ -5196,7 +4997,7 @@ gtk_label_destroy_window (GtkLabel *label)
   if (priv->select_info->window == NULL)
     return;
 
-  gdk_window_set_user_data (priv->select_info->window, NULL);
+  gtk_widget_unregister_window (GTK_WIDGET (label), priv->select_info->window);
   gdk_window_destroy (priv->select_info->window);
   priv->select_info->window = NULL;
 }
@@ -5376,7 +5177,8 @@ gtk_label_set_selection_text (GtkLabel         *label,
 {
   GtkLabelPrivate *priv = label->priv;
 
-  if ((priv->select_info->selection_anchor !=
+  if (priv->select_info &&
+      (priv->select_info->selection_anchor !=
        priv->select_info->selection_end) &&
       priv->text)
     {
@@ -5455,14 +5257,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)
         {
@@ -5474,26 +5286,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));
     }
 }
@@ -5675,8 +5486,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));
 }
 
 /**
@@ -5711,8 +5526,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));
 }
 
 /**
@@ -5992,9 +5811,9 @@ 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)
 {
   GtkLabelPrivate *priv = label->priv;
   gint old_pos;
@@ -6012,53 +5831,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)
@@ -6080,41 +5899,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);
 }
@@ -6384,67 +6203,11 @@ gtk_label_clear_links (GtkLabel *label)
   if (!priv->select_info)
     return;
 
-  g_list_foreach (priv->select_info->links, (GFunc)link_free, NULL);
-  g_list_free (priv->select_info->links);
+  g_list_free_full (priv->select_info->links, (GDestroyNotify) link_free);
   priv->select_info->links = NULL;
   priv->select_info->active_link = NULL;
 }
 
-static void
-gtk_label_rescan_links (GtkLabel *label)
-{
-  GtkLabelPrivate *priv = label->priv;
-  PangoLayout *layout = priv->layout;
-  PangoAttrList *attlist;
-  PangoAttrIterator *iter;
-  GList *links;
-
-  if (!priv->select_info || !priv->select_info->links)
-    return;
-
-  attlist = pango_layout_get_attributes (layout);
-
-  if (attlist == NULL)
-    return;
-
-  iter = pango_attr_list_get_iterator (attlist);
-
-  links = priv->select_info->links;
-
-  do
-    {
-      PangoAttribute *underline;
-      PangoAttribute *color;
-
-      underline = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
-      color = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
-
-      if (underline != NULL && color != NULL)
-        {
-          gint start, end;
-          PangoRectangle start_pos;
-          PangoRectangle end_pos;
-          GtkLabelLink *link;
-
-          pango_attr_iterator_range (iter, &start, &end);
-          pango_layout_index_to_pos (layout, start, &start_pos);
-          pango_layout_index_to_pos (layout, end, &end_pos);
-
-          if (links == NULL)
-            {
-              g_warning ("Ran out of links");
-              break;
-            }
-          link = links->data;
-          links = links->next;
-          link->start = start;
-          link->end = end;
-        }
-      } while (pango_attr_iterator_next (iter));
-
-    pango_attr_iterator_destroy (iter);
-}
-
 static gboolean
 gtk_label_activate_link (GtkLabel    *label,
                          const gchar *uri)
@@ -6474,7 +6237,7 @@ emit_activate_link (GtkLabel     *label,
     {
       link->visited = TRUE;
       /* FIXME: shouldn't have to redo everything here */
-      gtk_label_recalculate (label);
+      gtk_label_clear_layout (label);
     }
 }
 
@@ -6548,7 +6311,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;
@@ -6664,3 +6427,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;
+}