diff --git a/docs/reference/gtk/tmpl/gtklabel.sgml b/docs/reference/gtk/tmpl/gtklabel.sgml
index 2fdc855..63ea1ae 100644
--- a/docs/reference/gtk/tmpl/gtklabel.sgml
+++ b/docs/reference/gtk/tmpl/gtklabel.sgml
@@ -162,6 +162,22 @@ aligns in its available space, see gtk_misc_set_alignment().
 
 </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 &lt;a href=\"http://www.gtk.org\" title=\"&amp;lt;i&amp;gt;Our&amp;/i&amp;gt; website\"&gt;GTK+ website</a> for more...");
+</programlisting></informalexample>
+</para>
+
+</refsect2>
+
 <!-- ##### SECTION See_Also ##### -->
 <para>
 
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 9e99266..dc63981 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -2210,6 +2210,7 @@ gtk_label_set_text_with_mnemonic
 gtk_label_set_use_markup
 gtk_label_set_use_underline
 gtk_label_set_width_chars
+gtk_label_get_current_uri
 #endif
 #endif
 
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 91ab5cc..570fe32 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -45,6 +45,9 @@
 #include "gtkstock.h"
 #include "gtkbindings.h"
 #include "gtkbuildable.h"
+#include "gtkimage.h"
+#include "gtkshow.h"
+#include "gtktooltip.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -55,27 +58,46 @@ typedef struct
   gint wrap_width;
   gint width_chars;
   gint max_width_chars;
-}
-GtkLabelPrivate;
+} GtkLabelPrivate;
+
+typedef struct
+{
+  gchar *uri;
+  gchar *title;
+  gboolean visited;
+} GtkLabelUri;
+
+typedef struct
+{
+  GtkLabelUri *uri;
+  gint start;
+  gint end;
+} GtkLabelLink;
 
 struct _GtkLabelSelectionInfo
 {
   GdkWindow *window;
+  GtkWidget *popup_menu;
   gint selection_anchor;
   gint selection_end;
-  GtkWidget *popup_menu;
-  
+
+  GList *links;
+  GList *uris;
+  GtkLabelLink *active_link;
+
   gint drag_start_x;
   gint drag_start_y;
 
   guint in_drag : 1;
   guint select_words : 1;
+  guint selectable : 1;
 };
 
 enum {
   MOVE_CURSOR,
   COPY_CLIPBOARD,
   POPULATE_POPUP,
+  URI_ACTIVATE,
   LAST_SIGNAL
 };
 
@@ -103,6 +125,9 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
+static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
+
 static void gtk_label_set_property      (GObject          *object,
 					 guint             prop_id,
 					 const GValue     *value,
@@ -125,6 +150,8 @@ static void gtk_label_direction_changed (GtkWidget        *widget,
 					 GtkTextDirection  previous_dir);
 static gint gtk_label_expose            (GtkWidget        *widget,
 					 GdkEventExpose   *event);
+static gboolean gtk_label_focus         (GtkWidget         *widget,
+                                         GtkDirectionType   direction);
 
 static void gtk_label_realize           (GtkWidget        *widget);
 static void gtk_label_unrealize         (GtkWidget        *widget);
@@ -137,8 +164,16 @@ static gboolean gtk_label_button_release    (GtkWidget        *widget,
 					     GdkEventButton   *event);
 static gboolean gtk_label_motion            (GtkWidget        *widget,
 					     GdkEventMotion   *event);
+static gboolean gtk_label_leave_notify      (GtkWidget        *widget,
+                                             GdkEventCrossing *event);
+
 static void     gtk_label_grab_focus        (GtkWidget        *widget);
 
+static gboolean gtk_label_query_tooltip     (GtkWidget        *widget,
+                                             gint              x,
+                                             gint              y,
+                                             gboolean          keyboard_tip,
+                                             GtkTooltip       *tooltip);
 
 static void gtk_label_set_text_internal          (GtkLabel      *label,
 						  gchar         *str);
@@ -162,9 +197,13 @@ static void gtk_label_hierarchy_changed          (GtkWidget     *widget,
 						  GtkWidget     *old_toplevel);
 static void gtk_label_screen_changed             (GtkWidget     *widget,
 						  GdkScreen     *old_screen);
+static gboolean gtk_label_popup_menu             (GtkWidget       *widget);
 
 static void gtk_label_create_window       (GtkLabel *label);
 static void gtk_label_destroy_window      (GtkLabel *label);
+static void gtk_label_ensure_select_info  (GtkLabel *label);
+static void gtk_label_clear_select_info   (GtkLabel *label);
+static void gtk_label_update_cursor       (GtkLabel *label);
 static void gtk_label_clear_layout        (GtkLabel *label);
 static void gtk_label_ensure_layout       (GtkLabel *label);
 static void gtk_label_invalidate_wrap_width (GtkLabel *label);
@@ -197,7 +236,7 @@ static void     gtk_label_buildable_custom_finished    (GtkBuildable     *builda
 							gpointer          user_data);
 
 
-/* For selectable lables: */
+/* For selectable labels: */
 static void gtk_label_move_cursor        (GtkLabel        *label,
 					  GtkMovementStep  step,
 					  gint             count,
@@ -206,12 +245,17 @@ static void gtk_label_copy_clipboard     (GtkLabel        *label);
 static void gtk_label_select_all         (GtkLabel        *label);
 static void gtk_label_do_popup           (GtkLabel        *label,
 					  GdkEventButton  *event);
-
 static gint gtk_label_move_forward_word  (GtkLabel        *label,
 					  gint             start);
 static gint gtk_label_move_backward_word (GtkLabel        *label,
 					  gint             start);
 
+/* For uri labels: */
+static void gtk_label_rescan_uris        (GtkLabel        *label);
+static void gtk_label_clear_links        (GtkLabel        *label);
+static void gtk_label_clear_uris         (GtkLabel        *label);
+static gboolean gtk_label_uri_activate   (GtkLabel        *label);
+
 static GQuark quark_angle = 0;
 
 static GtkBuildableIface *buildable_parent_iface = NULL;
@@ -258,11 +302,12 @@ gtk_label_class_init (GtkLabelClass *class)
   gobject_class->finalize = gtk_label_finalize;
 
   object_class->destroy = gtk_label_destroy;
-  
+
   widget_class->size_request = gtk_label_size_request;
   widget_class->size_allocate = gtk_label_size_allocate;
   widget_class->state_changed = gtk_label_state_changed;
   widget_class->style_set = gtk_label_style_set;
+  widget_class->query_tooltip = gtk_label_query_tooltip;
   widget_class->direction_changed = gtk_label_direction_changed;
   widget_class->expose_event = gtk_label_expose;
   widget_class->realize = gtk_label_realize;
@@ -272,15 +317,19 @@ gtk_label_class_init (GtkLabelClass *class)
   widget_class->button_press_event = gtk_label_button_press;
   widget_class->button_release_event = gtk_label_button_release;
   widget_class->motion_notify_event = gtk_label_motion;
+  widget_class->leave_notify_event = gtk_label_leave_notify;
   widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
   widget_class->screen_changed = gtk_label_screen_changed;
   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
   widget_class->drag_data_get = gtk_label_drag_data_get;
   widget_class->grab_focus = gtk_label_grab_focus;
+  widget_class->popup_menu = gtk_label_popup_menu;
+  widget_class->focus = gtk_label_focus;
 
   class->move_cursor = gtk_label_move_cursor;
   class->copy_clipboard = gtk_label_copy_clipboard;
-  
+  class->uri_activate = gtk_label_uri_activate;
+
   /**
    * GtkLabel::move-cursor:
    * @entry: the object which received the signal
@@ -361,6 +410,35 @@ gtk_label_class_init (GtkLabelClass *class)
 		  G_TYPE_NONE, 1,
 		  GTK_TYPE_MENU);
 
+    /**
+     * GtkLabel::uri-activate:
+     * @label: The label on which the signal was emitted.
+     *
+     * A  <link linkend="keybinding-signals">keybinding signal</link>
+     * which gets emitted when the user activates a URI in the label.
+     *
+     * Applications may connect to it to override the default behaviour,
+     * which is to call gtk_show_uri(). To obtain the URI, use
+     * gtk_label_get_current_uri().
+     *
+     * Applications may also emit the signal with g_signal_emit_by_name()
+     * if they need to control activation of URIs programmatically.
+     *
+     * The default bindings for this signal are all forms of the Enter key.
+     *
+     * Returns: %TRUE if the URI has been activated
+     *
+     * Since: 2.18
+     */
+    signals[URI_ACTIVATE] =
+      g_signal_new ("uri-activate",
+                    G_TYPE_FROM_CLASS (object_class),
+                    G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                    G_STRUCT_OFFSET (GtkLabelClass, uri_activate),
+                    _gtk_boolean_handled_accumulator, NULL,
+                    _gtk_marshal_BOOLEAN__VOID,
+                    G_TYPE_BOOLEAN, 0);
+
   g_object_class_install_property (gobject_class,
                                    PROP_LABEL,
                                    g_param_spec_string ("label",
@@ -691,6 +769,13 @@ gtk_label_class_init (GtkLabelClass *class)
   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
 				"copy-clipboard", 0);
 
+  gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
+				"uri-activate", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
+				"uri-activate", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
+				"uri-activate", 0);
+
   gtk_settings_install_property (g_param_spec_boolean ("gtk-label-select-on-focus",
 						       P_("Select on focus"),
 						       P_("Whether to select the contents of a selectable label when it is focused"),
@@ -1664,7 +1749,7 @@ gtk_label_recalculate (GtkLabel *label)
       g_object_notify (G_OBJECT (label), "mnemonic-keyval");
     }
 
-  gtk_label_clear_layout (label);  
+  gtk_label_clear_layout (label);
   gtk_widget_queue_resize (GTK_WIDGET (label));
 }
 
@@ -1788,6 +1873,287 @@ gtk_label_get_label (GtkLabel *label)
   return label->label;
 }
 
+typedef struct
+{
+  GtkLabel *label;
+  GList *uris;
+  GString *new_str;
+  GdkColor *link_color;
+  GdkColor *visited_link_color;
+} UriParserData;
+
+static void
+start_element_handler (GMarkupParseContext  *context,
+                       const gchar          *element_name,
+                       const gchar         **attribute_names,
+                       const gchar         **attribute_values,
+                       gpointer              user_data,
+                       GError              **error)
+{
+  UriParserData *pdata = user_data;
+
+  if (strcmp (element_name, "a") == 0)
+    {
+      GtkLabelUri *udata;
+      const gchar *uri = NULL;
+      const gchar *title = NULL;
+      gboolean visited = FALSE;
+      gint line_number;
+      gint char_number;
+      gint i;
+      GdkColor *color = NULL;
+
+      g_markup_parse_context_get_position (context, &line_number, &char_number);
+
+      for (i = 0; attribute_names[i] != NULL; i++)
+        {
+          const gchar *attr = attribute_names[i];
+
+          if (strcmp (attr, "href") == 0)
+            uri = attribute_values[i];
+          else if (strcmp (attr, "title") == 0)
+            title = attribute_values[i];
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           "Attribute '%s' is not allowed on the <a> tag "
+                           "on line %d char %d",
+                            attr, line_number, char_number);
+              return;
+            }
+        }
+
+      if (uri == NULL)
+        {
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_INVALID_CONTENT,
+                       "Attribute 'href' was missing on the <a> tag "
+                       "on line %d char %d",
+                       line_number, char_number);
+          return;
+        }
+
+      if (pdata->label->select_info)
+        {
+          GList *l;
+          for (l = pdata->label->select_info->uris; l; l = l->next)
+            {
+              udata = l->data;
+              if (strcmp (uri, udata->uri) == 0)
+                {
+                  visited = udata->visited;
+                  break;
+                }
+            }
+        }
+
+      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);
+
+      udata = g_new0 (GtkLabelUri, 1);
+      udata->uri = g_strdup (uri);
+      udata->title = g_strdup (title);
+      udata->visited = visited;
+      pdata->uris = g_list_append (pdata->uris, udata);
+    }
+  else
+    {
+      gint i;
+
+      g_string_append_c (pdata->new_str, '<');
+      g_string_append (pdata->new_str, element_name);
+
+      for (i = 0; attribute_names[i] != NULL; i++)
+        {
+          const gchar *attr  = attribute_names[i];
+          const gchar *value = attribute_values[i];
+          gchar *newvalue;
+
+          newvalue = g_markup_escape_text (value, -1);
+
+          g_string_append_c (pdata->new_str, ' ');
+          g_string_append (pdata->new_str, attr);
+          g_string_append (pdata->new_str, "=\"");
+          g_string_append (pdata->new_str, newvalue);
+          g_string_append_c (pdata->new_str, '\"');
+
+          g_free (newvalue);
+        }
+      g_string_append_c (pdata->new_str, '>');
+    }
+}
+
+static void
+end_element_handler (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     gpointer              user_data,
+                     GError              **error)
+{
+  UriParserData *pdata = user_data;
+
+  if (!strcmp (element_name, "a"))
+    {
+      g_string_append (pdata->new_str, "</span>");
+    }
+  else
+    {
+      g_string_append_printf (pdata->new_str,
+                              "</%s>", element_name);
+    }
+}
+
+static void
+text_handler (GMarkupParseContext  *context,
+              const gchar          *text,
+              gsize                 text_len,
+              gpointer              user_data,
+              GError              **error)
+{
+  UriParserData *pdata = user_data;
+
+  gchar *newtext = g_markup_escape_text (text, text_len);
+  g_string_append_len (pdata->new_str, newtext, strlen (newtext));
+  g_free (newtext);
+}
+
+static const GMarkupParser markup_parser =
+{
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  NULL
+};
+
+static gboolean
+xml_isspace (gchar c)
+{
+  return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+}
+
+static void
+uri_data_free (gpointer data)
+{
+  GtkLabelUri *udata = data;
+
+  g_free (udata->uri);
+  g_free (udata->title);
+  g_free (udata);
+}
+
+static gboolean
+parse_uri_markup (GtkLabel     *label,
+                  const gchar  *str,
+                  gchar       **new_str,
+                  GList       **uris,
+                  GError      **error)
+{
+  GMarkupParseContext *context = NULL;
+  const gchar *p, *end;
+  gboolean needs_root = TRUE;
+  gsize length;
+  UriParserData pdata;
+
+  length = strlen (str);
+  p = str;
+  end = str + length;
+
+  pdata.label = label;
+  pdata.uris = NULL;
+  pdata.new_str = g_string_sized_new (length);
+
+  gtk_widget_ensure_style (GTK_WIDGET (label));
+  gtk_widget_style_get (GTK_WIDGET (label),
+                        "link-color", &pdata.link_color,
+                        "visited-link-color", &pdata.visited_link_color,
+                        NULL);
+  if (!pdata.link_color)
+    pdata.link_color = (GdkColor *) &default_link_color;
+  if (!pdata.visited_link_color)
+    pdata.visited_link_color = (GdkColor *) &default_visited_link_color;
+
+  while (p != end && xml_isspace (*p))
+    p++;
+
+  if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
+    needs_root = FALSE;
+
+  context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
+
+  if (needs_root)
+    {
+      if (!g_markup_parse_context_parse (context, "<markup>", -1, error))
+        goto failed;
+    }
+
+  if (!g_markup_parse_context_parse (context, str, length, error))
+    goto failed;
+
+  if (needs_root)
+    {
+      if (!g_markup_parse_context_parse (context, "</markup>", -1, error))
+        goto failed;
+    }
+
+  if (!g_markup_parse_context_end_parse (context, error))
+    goto failed;
+
+  g_markup_parse_context_free (context);
+
+  *new_str = g_string_free (pdata.new_str, FALSE);
+  *uris = pdata.uris;
+
+  if (pdata.link_color != &default_link_color)
+    gdk_color_free (pdata.link_color);
+  if (pdata.visited_link_color != &default_visited_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.uris, (GFunc)uri_data_free, NULL);
+  g_list_free (pdata.uris);
+
+  if (pdata.link_color != &default_link_color)
+    gdk_color_free (pdata.link_color);
+  if (pdata.visited_link_color != &default_visited_link_color)
+    gdk_color_free (pdata.visited_link_color);
+
+  return FALSE;
+}
+
+static void
+gtk_label_ensure_has_tooltip (GtkLabel *label)
+{
+  GList *l;
+  gboolean has_tooltip = FALSE;
+
+  for (l = label->select_info->uris; l; l = l->next)
+    {
+      GtkLabelUri *udata = l->data;
+      if (udata->title)
+        {
+          has_tooltip = TRUE;
+          break;
+        }
+    }
+
+  gtk_widget_set_has_tooltip (GTK_WIDGET (label), has_tooltip);
+}
+
 static void
 set_markup (GtkLabel    *label,
             const gchar *str,
@@ -1797,8 +2163,26 @@ set_markup (GtkLabel    *label,
   GError *error = NULL;
   PangoAttrList *attrs = NULL;
   gunichar accel_char = 0;
+  gchar *new_str;
+  GList *uris = NULL;
+
+  if (!parse_uri_markup (label, str, &new_str, &uris, &error))
+    {
+      g_warning ("Failed to set text from markup due to error parsing markup: %s",
+                 error->message);
+      g_error_free (error);
+      return;
+    }
+
+  gtk_label_clear_uris (label);
+  if (uris)
+    {
+      gtk_label_ensure_select_info (label);
+      label->select_info->uris = uris;
+      gtk_label_ensure_has_tooltip (label);
+    }
 
-  if (!pango_parse_markup (str,
+  if (!pango_parse_markup (new_str,
                            -1,
                            with_uline ? '_' : 0,
                            &attrs,
@@ -1808,10 +2192,13 @@ set_markup (GtkLabel    *label,
     {
       g_warning ("Failed to set text from markup due to error parsing markup: %s",
                  error->message);
+      g_free (new_str);
       g_error_free (error);
       return;
     }
 
+  g_free (new_str);
+
   if (text)
     gtk_label_set_text_internal (label, text);
 
@@ -2320,6 +2707,9 @@ gtk_label_finalize (GObject *object)
   if (label->effective_attrs)
     pango_attr_list_unref (label->effective_attrs);
 
+  gtk_label_clear_links (label);
+  gtk_label_clear_uris (label);
+
   g_free (label->select_info);
 
   G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
@@ -2332,6 +2722,8 @@ gtk_label_clear_layout (GtkLabel *label)
     {
       g_object_unref (label->layout);
       label->layout = NULL;
+
+      gtk_label_clear_links (label);
     }
 }
 
@@ -2417,7 +2809,7 @@ gtk_label_ensure_layout (GtkLabel *label)
 
   widget = GTK_WIDGET (label);
 
-  rtl = gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL;
+  rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
 
   if (!label->layout)
     {
@@ -2450,7 +2842,9 @@ gtk_label_ensure_layout (GtkLabel *label)
 
       if (label->effective_attrs)
 	pango_layout_set_attributes (label->layout, label->effective_attrs);
-      
+
+      gtk_label_rescan_uris (label);
+
       switch (label->jtype)
 	{
 	case GTK_JUSTIFY_LEFT:
@@ -2676,37 +3070,54 @@ gtk_label_size_allocate (GtkWidget     *widget,
 }
 
 static void
-gtk_label_state_changed (GtkWidget   *widget,
-                         GtkStateType prev_state)
+gtk_label_update_cursor (GtkLabel *label)
 {
-  GtkLabel *label;
-  GdkCursor *cursor;
-  
-  label = GTK_LABEL (widget);
+  if (!label->select_info)
+    return;
 
-  if (label->select_info)
+  if (GTK_WIDGET_REALIZED (label))
     {
-      gtk_label_select_region (label, 0, 0);
+      GdkDisplay *display;
+      GdkCursor *cursor;
 
-      if (GTK_WIDGET_REALIZED (widget))
+      if (GTK_WIDGET_IS_SENSITIVE (label))
         {
-          if (GTK_WIDGET_IS_SENSITIVE (widget))
-            cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_XTERM);
+          display = gtk_widget_get_display (GTK_WIDGET (label));
+
+          if (label->select_info->active_link)
+            cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
+          else if (label->select_info->selectable)
+            cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
           else
             cursor = NULL;
+        }
+      else
+        cursor = NULL;
 
-          gdk_window_set_cursor (label->select_info->window, cursor);
+      gdk_window_set_cursor (label->select_info->window, cursor);
 
-          if (cursor)
-            gdk_cursor_unref (cursor);
-        }
+      if (cursor)
+        gdk_cursor_unref (cursor);
+    }
+}
+
+static void
+gtk_label_state_changed (GtkWidget   *widget,
+                         GtkStateType prev_state)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+
+  if (label->select_info)
+    {
+      gtk_label_select_region (label, 0, 0);
+      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 
+static void
 gtk_label_style_set (GtkWidget *widget,
 		     GtkStyle  *previous_style)
 {
@@ -2962,7 +3373,7 @@ gtk_label_expose (GtkWidget      *widget,
                                                    range,
                                                    1);
 	  gdk_region_intersect (clip, event->region);
- 
+
          /* FIXME should use gtk_paint, but it can't use a clip
            * region
            */
@@ -2973,7 +3384,7 @@ gtk_label_expose (GtkWidget      *widget,
 	  state = GTK_STATE_SELECTED;
 	  if (!GTK_WIDGET_HAS_FOCUS (widget))
 	    state = GTK_STATE_ACTIVE;
-	      
+
           gdk_draw_layout_with_colors (widget->window,
                                        widget->style->black_gc,
                                        x, y,
@@ -2985,7 +3396,30 @@ gtk_label_expose (GtkWidget      *widget,
           gdk_region_destroy (clip);
         }
       else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget))
-	gtk_label_draw_cursor (label, x, y);
+        {
+          if (label->select_info->selectable)
+	    gtk_label_draw_cursor (label, x, y);
+
+          if (label->select_info->active_link)
+            {
+              gint range[2];
+              GdkRegion *clip;
+              GdkRectangle rect;
+
+              range[0] = label->select_info->active_link->start;
+              range[1] = label->select_info->active_link->end;
+
+              clip = gdk_pango_layout_get_clip_region (label->layout,
+                                                       x, y,
+                                                       range,
+                                                       1);
+              gdk_region_get_clipbox (clip, &rect);
+              gdk_region_destroy (clip);
+              gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
+                               &event->area, widget, "label",
+                               rect.x, rect.y, rect.width, rect.height);
+            }
+        }
     }
 
   return FALSE;
@@ -3245,7 +3679,7 @@ layout_to_window_coords (GtkLabel *label,
 }
 #endif
 
-static void
+static gboolean
 get_layout_index (GtkLabel *label,
                   gint      x,
                   gint      y,
@@ -3256,28 +3690,32 @@ get_layout_index (GtkLabel *label,
   const gchar *cluster_end;
 
   *index = 0;
-  
+
   gtk_label_ensure_layout (label);
-  
+
   window_to_layout_coords (label, &x, &y);
 
   x *= PANGO_SCALE;
   y *= PANGO_SCALE;
-  
-  pango_layout_xy_to_index (label->layout,
-                            x, y,
-                            index, &trailing);
 
-  
-  cluster = label->text + *index;
-  cluster_end = cluster;
-  while (trailing)
+  if (pango_layout_xy_to_index (label->layout,
+                                x, y,
+                                index, &trailing))
     {
-      cluster_end = g_utf8_next_char (cluster_end);
-      --trailing;
+      cluster = label->text + *index;
+      cluster_end = cluster;
+      while (trailing)
+        {
+          cluster_end = g_utf8_next_char (cluster_end);
+          --trailing;
+        }
+
+      *index += (cluster_end - cluster);
+
+      return TRUE;
     }
 
-  *index += (cluster_end - cluster);
+  return FALSE;
 }
 
 static void
@@ -3304,7 +3742,7 @@ gtk_label_grab_focus (GtkWidget *widget)
 {
   GtkLabel *label;
   gboolean select_on_focus;
-  
+
   label = GTK_LABEL (widget);
 
   if (label->select_info == NULL)
@@ -3312,34 +3750,167 @@ gtk_label_grab_focus (GtkWidget *widget)
 
   GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
 
-  g_object_get (gtk_widget_get_settings (widget),
-		"gtk-label-select-on-focus",
-		&select_on_focus,
-		NULL);
-  
-  if (select_on_focus && !label->in_click)
-    gtk_label_select_region (label, 0, -1);
+  if (label->select_info->selectable)
+    {
+      g_object_get (gtk_widget_get_settings (widget),
+                    "gtk-label-select-on-focus",
+                    &select_on_focus,
+                    NULL);
+
+      if (select_on_focus && !label->in_click)
+        gtk_label_select_region (label, 0, -1);
+    }
+  else
+    {
+      if (label->select_info->links)
+        label->select_info->active_link = label->select_info->links->data;
+    }
 }
- 
+
+static gboolean
+gtk_label_focus (GtkWidget        *widget,
+                 GtkDirectionType  direction)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GList *l;
+
+  if (!gtk_widget_is_focus (widget))
+    {
+      gtk_widget_grab_focus (widget);
+      if (label->select_info && label->select_info->active_link)
+        {
+          if (direction == GTK_DIR_TAB_BACKWARD)
+            {
+              l = g_list_last (label->select_info->links);
+              label->select_info->active_link = l->data;
+            }
+        }
+
+      return TRUE;
+    }
+
+  if (!label->select_info)
+    return FALSE;
+
+  if (label->select_info->selectable)
+    {
+      gint index;
+
+      if (label->select_info->selection_anchor != label->select_info->selection_end)
+        goto out;
+
+      index = label->select_info->selection_anchor;
+
+      if (direction == GTK_DIR_TAB_FORWARD)
+        for (l = label->select_info->links; l; l = l->next)
+          {
+            GtkLabelLink *link = l->data;
+
+            if (link->start > index)
+              {
+                gtk_label_select_region_index (label, link->start, link->start);
+                return TRUE;
+              }
+          }
+      else if (direction == GTK_DIR_TAB_BACKWARD)
+        for (l = g_list_last (label->select_info->links); l; l = l->prev)
+          {
+            GtkLabelLink *link = l->data;
+
+            if (link->end < index)
+              {
+                gtk_label_select_region_index (label, link->start, link->start);
+                return TRUE;
+              }
+          }
+
+      goto out;
+    }
+  else
+    {
+      switch (direction)
+        {
+        case GTK_DIR_TAB_FORWARD:
+          if (label->select_info->active_link)
+            {
+              l = g_list_find (label->select_info->links, label->select_info->active_link);
+              l = l->next;
+            }
+          else
+            l = label->select_info->links;
+          break;
+
+        case GTK_DIR_TAB_BACKWARD:
+          if (label->select_info->active_link)
+            {
+              l = g_list_find (label->select_info->links, label->select_info->active_link);
+              l = l->prev;
+            }
+          else
+            l = g_list_last (label->select_info->links);
+          break;
+
+        default:
+          goto out;
+        }
+
+      if (l)
+        {
+          label->select_info->active_link = l->data;
+          gtk_widget_queue_draw (widget);
+
+          return TRUE;
+        }
+    }
+
+out:
+  label->select_info->active_link = NULL;
+
+  return FALSE;
+}
+
 static gboolean
 gtk_label_button_press (GtkWidget      *widget,
                         GdkEventButton *event)
 {
-  GtkLabel *label;
+  GtkLabel *label = GTK_LABEL (widget);
   gint index = 0;
-  gint min, max;  
-  
-  label = GTK_LABEL (widget);
+  gint min, max;
 
   if (label->select_info == NULL)
     return FALSE;
 
+  if (label->select_info->active_link)
+    {
+      if (event->button == 1)
+        {
+          gboolean handled;
+
+          g_signal_emit (label, signals[URI_ACTIVATE], 0, &handled);
+          if (handled && !label->select_info->active_link->uri->visited)
+            {
+              label->select_info->active_link->uri->visited = TRUE;
+              /* FIXME: shouldn't have to redo everything here */
+              gtk_label_recalculate (label);
+            }
+        }
+      else if (event->button == 3)
+        {
+          gtk_label_do_popup (label, event);
+        }
+
+      return TRUE;
+    }
+
+  if (!label->select_info->selectable)
+    return FALSE;
+
   label->select_info->in_drag = FALSE;
   label->select_info->select_words = FALSE;
 
   if (event->button == 1)
     {
-      if (!GTK_WIDGET_HAS_FOCUS (widget)) 
+      if (!GTK_WIDGET_HAS_FOCUS (widget))
 	{
 	  label->in_click = TRUE;
 	  gtk_widget_grab_focus (widget);
@@ -3351,21 +3922,21 @@ gtk_label_button_press (GtkWidget      *widget,
 	  gtk_label_select_region_index (label, 0, strlen (label->text));
 	  return TRUE;
 	}
-      
+
       if (event->type == GDK_2BUTTON_PRESS)
 	{
           label->select_info->select_words = TRUE;
 	  gtk_label_select_word (label);
 	  return TRUE;
 	}
-      
+
       get_layout_index (label, event->x, event->y, &index);
-      
+
       min = MIN (label->select_info->selection_anchor,
 		 label->select_info->selection_end);
       max = MAX (label->select_info->selection_anchor,
 		 label->select_info->selection_end);
-	  
+
       if ((label->select_info->selection_anchor !=
 	   label->select_info->selection_end) &&
 	  (event->state & GDK_SHIFT_MASK))
@@ -3373,7 +3944,7 @@ gtk_label_button_press (GtkWidget      *widget,
 	  /* extend (same as motion) */
 	  min = MIN (min, index);
 	  max = MAX (max, index);
-	  
+
 	  /* ensure the anchor is opposite index */
 	  if (index == min)
 	    {
@@ -3381,10 +3952,10 @@ gtk_label_button_press (GtkWidget      *widget,
 	      min = max;
 	      max = tmp;
 	    }
-	  
+
 	  gtk_label_select_region_index (label, min, max);
 	}
-      else 
+      else
 	{
 	  if (event->type == GDK_3BUTTON_PRESS)
 	    gtk_label_select_region_index (label, 0, strlen (label->text));
@@ -3400,7 +3971,7 @@ gtk_label_button_press (GtkWidget      *widget,
 	    /* start a replacement */
 	    gtk_label_select_region_index (label, index, index);
 	}
-  
+
       return TRUE;
     }
   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
@@ -3408,7 +3979,6 @@ gtk_label_button_press (GtkWidget      *widget,
       gtk_label_do_popup (label, event);
 
       return TRUE;
-      
     }
   return FALSE;
 }
@@ -3420,27 +3990,26 @@ gtk_label_button_release (GtkWidget      *widget,
 {
   GtkLabel *label = GTK_LABEL (widget);
   gint index;
-  
+
   if (label->select_info == NULL)
     return FALSE;
-  
+
   if (label->select_info->in_drag)
     {
       label->select_info->in_drag = 0;
 
       get_layout_index (label, event->x, event->y, &index);
       gtk_label_select_region_index (label, index, index);
-      
+
       return FALSE;
     }
 
   if (event->button != 1)
     return FALSE;
-  
+
   /* The goal here is to return TRUE iff we ate the
    * button press to start selecting.
    */
-  
   return TRUE;
 }
 
@@ -3462,7 +4031,7 @@ drag_begin_cb (GtkWidget      *widget,
     {
       gint start, end;
       gint len;
-      
+
       start = MIN (label->select_info->selection_anchor,
                    label->select_info->selection_end);
       end = MAX (label->select_info->selection_anchor,
@@ -3498,47 +4067,93 @@ static gboolean
 gtk_label_motion (GtkWidget      *widget,
                   GdkEventMotion *event)
 {
-  GtkLabel *label;
+  GtkLabel *label = GTK_LABEL (widget);
   gint index;
   gint x, y;
-  
-  label = GTK_LABEL (widget);
-  
+
   if (label->select_info == NULL)
-    return FALSE;  
+    return FALSE;
+
+  if (label->select_info->links &&
+      label->select_info->selection_anchor == label->select_info->selection_end &&
+      !label->select_info->in_drag)
+    {
+      GList *l;
+      GtkLabelLink *link;
+      gboolean found = FALSE;
+
+      gdk_window_get_pointer (event->window, &x, &y, NULL);
+      if (get_layout_index (label, x, y, &index))
+        {
+          for (l = label->select_info->links; l != NULL; l = l->next)
+            {
+              link = (GtkLabelLink *)l->data;
+
+              if (index >= link->start && index <= link->end)
+                {
+                  found = TRUE;
+                  break;
+                }
+            }
+        }
+
+      if (found)
+        {
+          if (label->select_info->active_link != link)
+            {
+              label->select_info->active_link = link;
+              gtk_label_update_cursor (label);
+            }
+
+          return TRUE;
+        }
+      else
+        {
+          if (label->select_info->active_link != NULL)
+            {
+              label->select_info->active_link = NULL;
+              gtk_label_update_cursor (label);
+
+              return TRUE;
+            }
+        }
+    }
+
+  if (!label->select_info->selectable)
+    return FALSE;
 
   if ((event->state & GDK_BUTTON1_MASK) == 0)
     return FALSE;
 
   gdk_window_get_pointer (label->select_info->window,
                           &x, &y, NULL);
-  
+ 
   if (label->select_info->in_drag)
     {
       if (gtk_drag_check_threshold (widget,
-				    label->select_info->drag_start_x, 
+				    label->select_info->drag_start_x,
 				    label->select_info->drag_start_y,
 				    event->x, event->y))
 	{
 	  GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
 
 	  gtk_target_list_add_text_targets (target_list, 0);
-	  
-          g_signal_connect (widget, "drag-begin", 
+
+          g_signal_connect (widget, "drag-begin",
                             G_CALLBACK (drag_begin_cb), NULL);
-	  gtk_drag_begin (widget, target_list, 
+	  gtk_drag_begin (widget, target_list,
 			  GDK_ACTION_COPY,
 			  1, (GdkEvent *)event);
-	  
+
 	  label->select_info->in_drag = FALSE;
-	  
+
 	  gtk_target_list_unref (target_list);
 	}
     }
   else
     {
       get_layout_index (label, x, y, &index);
-      
+
       if (label->select_info->select_words)
         {
           gint min, max;
@@ -3578,7 +4193,7 @@ gtk_label_motion (GtkWidget      *widget,
           gtk_label_select_region_index (label, anchor, end);
         }
       else
-        gtk_label_select_region_index (label, 
+        gtk_label_select_region_index (label,
                                        label->select_info->selection_anchor,
                                        index);
     }
@@ -3586,6 +4201,27 @@ gtk_label_motion (GtkWidget      *widget,
   return TRUE;
 }
 
+static gboolean
+gtk_label_leave_notify (GtkWidget        *widget,
+                        GdkEventCrossing *event)
+{
+  if (GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event)
+    GTK_WIDGET_CLASS (gtk_label_parent_class)->leave_notify_event (widget, event);
+
+  if (event->mode == GDK_CROSSING_NORMAL)
+    {
+      GtkLabel *label = GTK_LABEL (widget);
+
+      if (label->select_info)
+        {
+          label->select_info->active_link = NULL;
+          gtk_label_update_cursor (label);
+        }
+    }
+
+ return FALSE;
+}
+
 static void
 gtk_label_create_window (GtkLabel *label)
 {
@@ -3611,8 +4247,11 @@ gtk_label_create_window (GtkLabel *label)
   attributes.event_mask = gtk_widget_get_events (widget) |
     GDK_BUTTON_PRESS_MASK        |
     GDK_BUTTON_RELEASE_MASK      |
-    GDK_BUTTON_MOTION_MASK;
-  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR; 
+    GDK_LEAVE_NOTIFY_MASK        |
+    GDK_BUTTON_MOTION_MASK       |
+    GDK_POINTER_MOTION_MASK      |
+    GDK_POINTER_MOTION_HINT_MASK;
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
   if (GTK_WIDGET_IS_SENSITIVE (widget))
     {
       attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
@@ -3636,12 +4275,43 @@ gtk_label_destroy_window (GtkLabel *label)
 
   if (label->select_info->window == NULL)
     return;
-  
+
   gdk_window_set_user_data (label->select_info->window, NULL);
   gdk_window_destroy (label->select_info->window);
   label->select_info->window = NULL;
 }
 
+static void
+gtk_label_ensure_select_info (GtkLabel *label)
+{
+  if (label->select_info == NULL)
+    {
+      label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
+
+      GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
+
+      if (GTK_WIDGET_REALIZED (label))
+	gtk_label_create_window (label);
+
+      if (GTK_WIDGET_MAPPED (label))
+        gdk_window_show (label->select_info->window);
+    }
+}
+
+static void
+gtk_label_clear_select_info (GtkLabel *label)
+{
+  if (!label->select_info->selectable && !label->select_info->uris)
+    {
+      gtk_label_destroy_window (label);
+
+      g_free (label->select_info);
+      label->select_info = NULL;
+
+      GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
+    }
+}
+
 /**
  * gtk_label_set_selectable:
  * @label: a #GtkLabel
@@ -3655,44 +4325,28 @@ gtk_label_set_selectable (GtkLabel *label,
                           gboolean  setting)
 {
   gboolean old_setting;
-  
+
   g_return_if_fail (GTK_IS_LABEL (label));
-  
+
   setting = setting != FALSE;
-  old_setting = label->select_info != NULL;
-  
+  old_setting = label->select_info && label->select_info->selectable;
+
   if (setting)
     {
-      if (label->select_info == NULL)
-        {
-          label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
-
-	  GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
-      
-          if (GTK_WIDGET_REALIZED (label))
-	    gtk_label_create_window (label);
-
-          if (GTK_WIDGET_MAPPED (label))
-            gdk_window_show (label->select_info->window);
-        }
+      gtk_label_ensure_select_info (label);
+      label->select_info->selectable = TRUE;
+      gtk_label_update_cursor (label);
     }
   else
     {
-      if (label->select_info)
+      if (old_setting)
         {
           /* unselect, to give up the selection */
           gtk_label_select_region (label, 0, 0);
-          
-          if (label->select_info->window)
-	    {
-	      gtk_label_destroy_window (label);
-	    }
-
-          g_free (label->select_info);
 
-          label->select_info = NULL;
-
-	  GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
+          label->select_info->selectable = FALSE;
+          gtk_label_clear_select_info (label);
+          gtk_label_update_cursor (label);
         }
     }
   if (setting != old_setting)
@@ -3719,7 +4373,7 @@ gtk_label_get_selectable (GtkLabel *label)
 {
   g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
 
-  return label->select_info != NULL;
+  return label->select_info && label->select_info->selectable;
 }
 
 static void
@@ -3875,7 +4529,7 @@ gtk_label_select_region_index (GtkLabel *label,
 {
   g_return_if_fail (GTK_IS_LABEL (label));
   
-  if (label->select_info)
+  if (label->select_info && label->select_info->selectable)
     {
       GtkClipboard *clipboard;
 
@@ -3887,8 +4541,8 @@ gtk_label_select_region_index (GtkLabel *label,
       label->select_info->selection_end = end_index;
 
       clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label),
-					    GDK_SELECTION_PRIMARY);      
-      
+					    GDK_SELECTION_PRIMARY);
+
       if (anchor_index != end_index)
         {
           GtkTargetList *list;
@@ -3914,6 +4568,25 @@ gtk_label_select_region_index (GtkLabel *label,
             gtk_clipboard_clear (clipboard);
         }
 
+      label->select_info->active_link = NULL;
+      if (label->select_info->links &&
+          label->select_info->selection_anchor == label->select_info->selection_end)
+        {
+          GList *l;
+          gint index = label->select_info->selection_anchor;
+
+          for (l = label->select_info->links; l != NULL; l = l->next)
+            {
+              GtkLabelLink *link = (GtkLabelLink *)l->data;
+
+              if (index >= link->start && index <= link->end)
+                {
+                  label->select_info->active_link = link;
+                  break;
+                }
+            }
+        }
+
       gtk_widget_queue_draw (GTK_WIDGET (label));
 
       g_object_freeze_notify (G_OBJECT (label));
@@ -4528,7 +5201,8 @@ gtk_label_copy_clipboard (GtkLabel *label)
     {
       gint start, end;
       gint len;
-      
+      GtkClipboard *clipboard;
+
       start = MIN (label->select_info->selection_anchor,
                    label->select_info->selection_end);
       end = MAX (label->select_info->selection_anchor,
@@ -4542,10 +5216,12 @@ gtk_label_copy_clipboard (GtkLabel *label)
       if (start > len)
         start = len;
 
+      clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
+
       if (start != end)
-	gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (label),
-							  GDK_SELECTION_CLIPBOARD),
-				label->text + start, end - start);
+	gtk_clipboard_set_text (clipboard, label->text + start, end - start);
+      else if (label->select_info->active_link)
+        gtk_clipboard_set_text (clipboard, label->select_info->active_link->uri->uri, -1);
     }
 }
 
@@ -4588,8 +5264,7 @@ static void
 popup_menu_detach (GtkWidget *attach_widget,
 		   GtkMenu   *menu)
 {
-  GtkLabel *label;
-  label = GTK_LABEL (attach_widget);
+  GtkLabel *label = GTK_LABEL (attach_widget);
 
   if (label->select_info)
     label->select_info->popup_menu = NULL;
@@ -4606,20 +5281,17 @@ popup_position_func (GtkMenu   *menu,
   GtkWidget *widget;
   GtkRequisition req;
   GdkScreen *screen;
-  
-  label = GTK_LABEL (user_data);  
+
+  label = GTK_LABEL (user_data);
   widget = GTK_WIDGET (label);
 
-  if (label->select_info == NULL)
-    return;
-  
   g_return_if_fail (GTK_WIDGET_REALIZED (label));
-  
+
   screen = gtk_widget_get_screen (widget);
-  gdk_window_get_origin (widget->window, x, y);      
+  gdk_window_get_origin (widget->window, x, y);
+
+  gtk_widget_size_request (GTK_WIDGET (menu), &req);
 
-  gtk_widget_size_request (label->select_info->popup_menu, &req);
-  
   *x += widget->allocation.width / 2;
   *y += widget->allocation.height;
 
@@ -4627,65 +5299,328 @@ popup_position_func (GtkMenu   *menu,
   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
 }
 
+static void
+open_link_activate_cb (GtkMenuItem *menu_item,
+                       GtkLabel    *label)
+{
+  gboolean handled;
+
+  if (!label->select_info || !label->select_info->active_link)
+    return;
+
+  g_signal_emit (label, signals[URI_ACTIVATE], 0, &handled);
+  if (handled && !label->select_info->active_link->uri->visited)
+    {
+      label->select_info->active_link->uri->visited = TRUE;
+      /* FIXME: shouldn't have to redo everything here */
+      gtk_label_recalculate (label);
+    }
+}
+
+static void
+copy_link_activate_cb (GtkMenuItem *menu_item,
+                       GtkLabel    *label)
+{
+  GtkClipboard *clipboard;
+
+  if (!label->select_info || !label->select_info->active_link)
+    return;
+
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD);
+  gtk_clipboard_set_text (clipboard, label->select_info->active_link->uri->uri, -1);
+}
+
+static gboolean
+gtk_label_popup_menu (GtkWidget *widget)
+{
+  gtk_label_do_popup (GTK_LABEL (widget), NULL);
+
+  return TRUE;
+}
 
 static void
 gtk_label_do_popup (GtkLabel       *label,
                     GdkEventButton *event)
 {
   GtkWidget *menuitem;
-  gboolean have_selection;
+  GtkWidget *menu;
+  GtkWidget *image;
 
-  if (label->select_info == NULL)
+  if (!label->select_info)
     return;
-    
+
   if (label->select_info->popup_menu)
     gtk_widget_destroy (label->select_info->popup_menu);
-  
-  label->select_info->popup_menu = gtk_menu_new ();
 
-  gtk_menu_attach_to_widget (GTK_MENU (label->select_info->popup_menu),
-                             GTK_WIDGET (label),
-                             popup_menu_detach);
+  label->select_info->popup_menu = menu = gtk_menu_new ();
 
-  have_selection =
-    label->select_info->selection_anchor != label->select_info->selection_end;
+  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
 
+  if (label->select_info->active_link)
+    {
+      /* Open Link */
+      menuitem = gtk_image_menu_item_new_with_mnemonic (_("_Open Link"));
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
 
-  append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_CUT, "cut-clipboard",
-                        FALSE);
-  append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_COPY, "copy-clipboard",
-                        have_selection);
-  append_action_signal (label, label->select_info->popup_menu, GTK_STOCK_PASTE, "paste-clipboard",
-                        FALSE);
-  
-  menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
-  gtk_widget_set_sensitive (menuitem, FALSE);
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
+      g_signal_connect (G_OBJECT (menuitem), "activate",
+                        G_CALLBACK (open_link_activate_cb), label);
 
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
-      
-  menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
-  g_signal_connect_swapped (menuitem, "activate",
-			    G_CALLBACK (gtk_label_select_all), label);
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (label->select_info->popup_menu), menuitem);
+      image = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
+      gtk_widget_show (image);
+      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
+
+      /* Copy Link Address */
+      menuitem = gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+      g_signal_connect (G_OBJECT (menuitem), "activate",
+                        G_CALLBACK (copy_link_activate_cb), label);
+
+      image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
+      gtk_widget_show (image);
+      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
+    }
+  else
+    {
+      gboolean have_selection;
+
+      have_selection =
+        label->select_info->selection_anchor != label->select_info->selection_end;
 
-  g_signal_emit (label,
-		 signals[POPULATE_POPUP],
-		 0,
-		 label->select_info->popup_menu);
+      append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
+      append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
+      append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
   
+      menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
+      gtk_widget_set_sensitive (menuitem, FALSE);
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+      menuitem = gtk_separator_menu_item_new ();
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+      menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
+      g_signal_connect_swapped (menuitem, "activate",
+			        G_CALLBACK (gtk_label_select_all), label);
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+    }
+
+  g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
+
   if (event)
-    gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
+    gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
                     NULL, NULL,
                     event->button, event->time);
   else
-    gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
-                    popup_position_func, label,
-                    0, gtk_get_current_event_time ());
+    {
+      gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+                      popup_position_func, label,
+                      0, gtk_get_current_event_time ());
+      gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+    }
+}
+
+static void
+gtk_label_clear_links (GtkLabel *label)
+{
+  if (!label->select_info)
+    return;
+
+  g_list_foreach (label->select_info->links, (GFunc)g_free, NULL);
+  g_list_free (label->select_info->links);
+  label->select_info->links = NULL;
+  label->select_info->active_link = NULL;
+}
+
+static void
+gtk_label_clear_uris (GtkLabel *label)
+{
+  if (!label->select_info)
+    return;
+
+  g_list_foreach (label->select_info->uris, (GFunc)uri_data_free, NULL);
+  g_list_free (label->select_info->uris);
+  label->select_info->uris = NULL;
+}
+
+static void
+gtk_label_rescan_uris (GtkLabel *label)
+{
+  PangoLayout *layout = label->layout;
+  PangoAttrList *attlist = pango_layout_get_attributes (layout);
+  PangoAttrIterator *iter;
+  GList *uris;
+
+  if (!label->select_info)
+    return;
+
+  if (attlist == NULL)
+    return;
+
+  gtk_label_clear_links (label);
+
+  iter = pango_attr_list_get_iterator (attlist);
+
+  uris = label->select_info->uris;
+
+  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;
+          GtkLabelUri *udata;
+          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 (uris == NULL)
+            {
+              g_warning ("Ran out of uris");
+              break;
+            }
+          udata = uris->data;
+
+          link = g_new0 (GtkLabelLink, 1);
+          link->start = start;
+          link->end = end;
+          link->uri = udata;
+          label->select_info->links = g_list_append (label->select_info->links, link);
+          uris = uris->next;
+        }
+      } while (pango_attr_iterator_next (iter));
+
+    pango_attr_iterator_destroy (iter);
+}
+
+static gboolean
+gtk_label_uri_activate (GtkLabel *label)
+{
+  GError *error = NULL;
+  const gchar *uri;
+
+  uri = gtk_label_get_current_uri (label);
+
+  if (uri)
+    {
+      if (!gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (label)),
+                         uri, gtk_get_current_event_time (), &error))
+        {
+          g_warning ("Unable to show '%s': %s", uri, error->message);
+          g_error_free (error);
+        }
+    }
+  else
+    {
+      GtkWidget *widget;
+      GtkWidget *toplevel;
+      GtkWindow *window;
+
+      widget = GTK_WIDGET (label);
+      toplevel = gtk_widget_get_toplevel (widget);
+      if (GTK_IS_WINDOW (toplevel))
+        {
+          window = GTK_WINDOW (toplevel);
+
+          if (window &&
+              window->default_widget != (GtkWidget*)label &&
+              !(widget == window->focus_widget &&
+                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
+            gtk_window_activate_default (window);
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * gtk_label_get_current_uri:
+ * @label: a #GtkLabel
+ *
+ * Returns the currently active URI in the label. The active URI is the
+ * one under the mouse pointer, or in a selectable label, the URI in which
+ * the text caret is currently positioned.
+ *
+ * This function is intended for use in a #GtkLabel::uri-activate handler
+ * or for use in a  #GtkWidget::query-tooltip handler.
+ *
+ * Returns: the currently active URI. The string is owned by GTK+ and must
+ *   not be freed or modified.
+ *
+ * Since: 2.18
+ */
+G_CONST_RETURN gchar *
+gtk_label_get_current_uri (GtkLabel *label)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
+
+  if (label->select_info && label->select_info->active_link)
+    return label->select_info->active_link->uri->uri;
+
+  return NULL;
+}
+
+
+static gboolean
+gtk_label_query_tooltip (GtkWidget  *widget,
+                         gint        x,
+                         gint        y,
+                         gboolean    keyboard_tip,
+                         GtkTooltip *tooltip)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  gint index = -1;
+  GList *l;
+
+  if (label->select_info && label->select_info->links)
+    {
+      if (keyboard_tip)
+        {
+          if (label->select_info->selection_anchor == label->select_info->selection_end)
+            index = label->select_info->selection_anchor;
+        }
+      else
+        {
+          if (!get_layout_index (label, x, y, &index))
+            index = -1;
+        }
+
+      if (index != -1)
+        {
+          for (l = label->select_info->links; l != NULL; l = l->next)
+            {
+              GtkLabelLink *link = (GtkLabelLink *)l->data;
+
+              if (index >= link->start && index <= link->end)
+                {
+                  if (link->uri->title)
+                    {
+                      gtk_tooltip_set_markup (tooltip, link->uri->title);
+                      return TRUE;
+                    }
+                  break;
+                }
+            }
+        }
+    }
+
+  return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
+                                                                   x, y,
+                                                                   keyboard_tip,
+                                                                   tooltip);
 }
 
 #define __GTK_LABEL_C__
diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h
index 59778ef..656aa80 100644
--- a/gtk/gtklabel.h
+++ b/gtk/gtklabel.h
@@ -96,11 +96,12 @@ struct _GtkLabelClass
   void (* populate_popup)   (GtkLabel       *label,
                              GtkMenu        *menu);
 
+  gboolean (* uri_activate) (GtkLabel       *label);
+
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
   void (*_gtk_reserved3) (void);
-  void (*_gtk_reserved4) (void);
 };
 
 GType                 gtk_label_get_type          (void) G_GNUC_CONST;
@@ -174,6 +175,8 @@ void         gtk_label_set_single_line_mode  (GtkLabel *label,
                                               gboolean single_line_mode);
 gboolean     gtk_label_get_single_line_mode  (GtkLabel *label);
 
+G_CONST_RETURN gchar *gtk_label_get_current_uri (GtkLabel *label);
+
 #ifndef GTK_DISABLE_DEPRECATED
 
 #define  gtk_label_set           gtk_label_set_text
diff --git a/tests/testgtk.c b/tests/testgtk.c
index 568bb1a..50daea4 100644
--- a/tests/testgtk.c
+++ b/tests/testgtk.c
@@ -2961,6 +2961,50 @@ create_selectable_control (GtkWidget *widget)
   return button;
 }
 
+static gboolean
+uri_activate_cb (GtkLabel *label, gpointer data)
+{
+  const gchar *uri;
+
+  uri = gtk_label_get_current_uri (label);
+  if (g_strcmp0 (uri, "foo") == 0)
+    {
+      GtkDialog *dialog;
+
+      dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (label)),
+                                       GTK_DIALOG_DESTROY_WITH_PARENT,
+                                       GTK_MESSAGE_INFO,
+                                       GTK_BUTTONS_OK,
+                                       "%s activated", uri);
+      gtk_dialog_run (dialog);
+      gtk_widget_destroy (dialog);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+pop_u_late (GtkLabel *label,
+            GtkMenu  *menu,
+            gpointer  data)
+{
+  const gchar *uri;
+
+  uri = gtk_label_get_current_uri (label);
+  g_print ("current uri: %s\n", uri);
+
+  if (g_strcmp0 (uri, "foo") == 0)
+    {
+      GtkWidget *item;
+
+      item = gtk_image_menu_item_new_from_stock (GTK_STOCK_EDIT, NULL);
+      gtk_widget_show (item);
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+    }
+}
+
 void create_labels (GtkWidget *widget)
 {
   static GtkWidget *window = NULL;
@@ -2997,7 +3041,10 @@ void create_labels (GtkWidget *widget)
       button = create_selectable_control (hbox);
 
       gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
-      
+
+      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+      gtk_widget_grab_default (button);
+
       vbox = gtk_vbox_new (FALSE, 5);
       
       gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
@@ -3010,8 +3057,12 @@ void create_labels (GtkWidget *widget)
       gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
 
       frame = gtk_frame_new ("Multi-line Label");
-      label = gtk_label_new ("This is a Multi-line label.\nSecond line\nThird line");
+      label = gtk_label_new ("This is a Multi-line label.\n<a href=\"foo\" title=\"foo title\">Second</a> line\nThird <a href=\"http://www.line.com\" title=\"&lt;b&gt;www.line.com&lt;/b&gt;\">line</a>");
+      gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
       gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+      g_signal_connect (label, "uri-activate", G_CALLBACK (uri_activate_cb), NULL);
+      g_signal_connect (label, "populate-popup", G_CALLBACK (pop_u_late), NULL);
+
       gtk_container_add (GTK_CONTAINER (frame), label);
       gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
 
@@ -3051,14 +3102,15 @@ void create_labels (GtkWidget *widget)
       vbox = gtk_vbox_new (FALSE, 5);
       gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
       frame = gtk_frame_new ("Line wrapped label");
-      label = gtk_label_new ("This is an example of a line-wrapped label.  It should not be taking "\
+      label = gtk_label_new ("<a href=\"bu\">This is an example of a line-wrapped label.  It should not be taking "\
 			     "up the entire             "/* big space to test spacing */\
 			     "width allocated to it, but automatically wraps the words to fit.  "\
 			     "The time has come, for all good men, to come to the aid of their party.  "\
 			     "The sixth sheik's six sheep's sick.\n"\
 			     "     It supports multiple paragraphs correctly, and  correctly   adds "\
-			     "many          extra  spaces. ");
+			     "many          extra  spaces.</a> ");
 
+      gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
       gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
       gtk_container_add (GTK_CONTAINER (frame), label);
       gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
