[gtk/wip/baedert/for-master: 39/57] label: Modernize source file




commit 23d8d672d6c36e016fe8ba85b82f4d21a9567340
Author: Timm Bäder <mail baedert org>
Date:   Fri Dec 25 12:05:28 2020 +0100

    label: Modernize source file
    
    Try to sort toplevel functions to minimize unnecessary function
    prototypes at the beginning of the file, get rid of all tabs and
    trailing whitespace.

 gtk/gtklabel.c        | 6180 ++++++++++++++++++++++++-------------------------
 gtk/gtklabelprivate.h |    4 +-
 2 files changed, 3019 insertions(+), 3165 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 19b0386c05..ef655b29e1 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -407,168 +407,31 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 static GQuark quark_mnemonics_visible_connected;
 
-static void gtk_label_set_property      (GObject          *object,
-                                        guint             prop_id,
-                                        const GValue     *value,
-                                        GParamSpec       *pspec);
-static void gtk_label_get_property      (GObject          *object,
-                                        guint             prop_id,
-                                        GValue           *value,
-                                        GParamSpec       *pspec);
-static void gtk_label_finalize          (GObject          *object);
-static void gtk_label_dispose           (GObject          *object);
-static void gtk_label_size_allocate     (GtkWidget        *widget,
-                                         int               width,
-                                         int               height,
-                                         int               baseline);
-static void gtk_label_state_flags_changed   (GtkWidget        *widget,
-                                             GtkStateFlags     prev_state);
-static void gtk_label_css_changed       (GtkWidget         *widget,
-                                         GtkCssStyleChange *change);
-static void gtk_label_snapshot          (GtkWidget         *widget,
-                                         GtkSnapshot       *snapshot);
-static gboolean gtk_label_focus         (GtkWidget         *widget,
-                                         GtkDirectionType   direction);
-
-static void gtk_label_unrealize         (GtkWidget        *widget);
-
-static void gtk_label_motion            (GtkEventControllerMotion *controller,
-                                         double                    x,
-                                         double                    y,
-                                         gpointer                  data);
-static void gtk_label_leave             (GtkEventControllerMotion *controller,
-                                         gpointer                  data);
-
-static gboolean gtk_label_grab_focus        (GtkWidget        *widget);
-
-static gboolean gtk_label_query_tooltip     (GtkWidget        *widget,
-                                             int               x,
-                                             int               y,
-                                             gboolean          keyboard_tip,
-                                             GtkTooltip       *tooltip);
-
-static void gtk_label_set_text_internal          (GtkLabel      *self,
-                                                 char          *str);
-static gboolean gtk_label_set_label_internal     (GtkLabel      *self,
-                                                 const char    *str);
-static gboolean gtk_label_set_use_markup_internal    (GtkLabel  *self,
-                                                      gboolean   val);
-static gboolean gtk_label_set_use_underline_internal (GtkLabel  *self,
-                                                      gboolean   val);
 static void gtk_label_set_markup_internal        (GtkLabel      *self,
-                                                 const char    *str,
-                                                 gboolean       with_uline);
+                                                  const char    *str,
+                                                  gboolean       with_uline);
 static void gtk_label_recalculate                (GtkLabel      *self);
-static void gtk_label_root                       (GtkWidget     *widget);
-static void gtk_label_unroot                     (GtkWidget     *widget);
-static void gtk_label_popup_menu                 (GtkWidget     *widget,
-                                                  const char    *action_name,
-                                                  GVariant      *parameters);
 static void gtk_label_do_popup                   (GtkLabel      *self,
                                                   double         x,
                                                   double         y);
-
 static void gtk_label_ensure_select_info  (GtkLabel *self);
 static void gtk_label_clear_select_info   (GtkLabel *self);
-static void gtk_label_update_cursor       (GtkLabel *self);
 static void gtk_label_clear_layout        (GtkLabel *self);
 static void gtk_label_ensure_layout       (GtkLabel *self);
 static void gtk_label_select_region_index (GtkLabel *self,
                                            int       anchor_index,
                                            int       end_index);
-
 static void gtk_label_update_active_link  (GtkWidget *widget,
                                            double     x,
                                            double     y);
-
-static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
-                                            gboolean           group_cycling);
 static void     gtk_label_setup_mnemonic    (GtkLabel          *self);
 
 static void     gtk_label_buildable_interface_init   (GtkBuildableIface  *iface);
-static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable       *buildable,
-                                                      GtkBuilder         *builder,
-                                                      GObject            *child,
-                                                      const char         *tagname,
-                                                      GtkBuildableParser *parser,
-                                                      gpointer           *data);
-
-static void     gtk_label_buildable_custom_finished  (GtkBuildable       *buildable,
-                                                      GtkBuilder         *builder,
-                                                      GObject            *child,
-                                                      const char         *tagname,
-                                                      gpointer            user_data);
-
 /* For selectable labels: */
 static void gtk_label_move_cursor        (GtkLabel        *self,
-                                         GtkMovementStep  step,
-                                         int              count,
-                                         gboolean         extend_selection);
-static void gtk_label_copy_clipboard     (GtkLabel        *self);
-static void gtk_label_select_all         (GtkLabel        *self);
-static int gtk_label_move_forward_word   (GtkLabel        *self,
-                                         int              start);
-static int gtk_label_move_backward_word  (GtkLabel        *self,
-                                         int              start);
-
-/* For links: */
-static void          gtk_label_clear_links      (GtkLabel  *self);
-static gboolean      gtk_label_activate_link    (GtkLabel    *self,
-                                                 const char *uri);
-static void          gtk_label_activate_current_link (GtkLabel *self);
-static void          emit_activate_link         (GtkLabel     *self,
-                                                 GtkLabelLink *link);
-
-/* Event controller callbacks */
-static void   gtk_label_click_gesture_pressed  (GtkGestureClick *gesture,
-                                                     int                   n_press,
-                                                     double                x,
-                                                     double                y,
-                                                     GtkLabel             *self);
-static void   gtk_label_click_gesture_released (GtkGestureClick *gesture,
-                                                     int                   n_press,
-                                                     double                x,
-                                                     double                y,
-                                                     GtkLabel             *self);
-static void   gtk_label_drag_gesture_begin          (GtkGestureDrag *gesture,
-                                                     double          start_x,
-                                                     double          start_y,
-                                                     GtkLabel       *self);
-static void   gtk_label_drag_gesture_update         (GtkGestureDrag *gesture,
-                                                     double          offset_x,
-                                                     double          offset_y,
-                                                     GtkLabel       *self);
-
-/* Actions */
-
-static void      gtk_label_activate_clipboard_copy       (GtkWidget  *self,
-                                                          const char *name,
-                                                          GVariant   *parameter);
-static void      gtk_label_activate_selection_select_all (GtkWidget  *self,
-                                                          const char *name,
-                                                          GVariant   *parameter);
-static void      gtk_label_activate_link_open            (GtkWidget  *self,
-                                                          const char *name,
-                                                          GVariant   *parameter);
-static void      gtk_label_activate_link_copy            (GtkWidget  *self,
-                                                          const char *name,
-                                                          GVariant   *parameter);
-static void      gtk_label_nop                           (GtkWidget  *self,
-                                                          const char *name,
-                                                          GVariant   *parameter);
-
-static void gtk_label_update_actions (GtkLabel *self);
-
-static GtkSizeRequestMode gtk_label_get_request_mode                (GtkWidget           *widget);
-static void     gtk_label_measure (GtkWidget     *widget,
-                                   GtkOrientation  orientation,
-                                   int             for_size,
-                                   int            *minimum,
-                                   int            *natural,
-                                   int            *minimum_baseline,
-                                   int            *natural_baseline);
-
-
+                                          GtkMovementStep  step,
+                                          int              count,
+                                          gboolean         extend_selection);
 
 static GtkBuildableIface *buildable_parent_iface = NULL;
 
@@ -578,2987 +441,3168 @@ G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_WIDGET,
 
 static void
 add_move_binding (GtkWidgetClass *widget_class,
-                 guint           keyval,
-                 guint           modmask,
-                 GtkMovementStep step,
-                 int             count)
+                  guint           keyval,
+                  guint           modmask,
+                  GtkMovementStep step,
+                  int             count)
 {
   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
-  
+
   gtk_widget_class_add_binding_signal (widget_class,
                                        keyval, modmask,
-                                      "move-cursor",
+                                       "move-cursor",
                                        "(iib)", step, count, FALSE);
 
   /* Selection-extending version */
   gtk_widget_class_add_binding_signal (widget_class,
                                        keyval, modmask | GDK_SHIFT_MASK,
-                                      "move-cursor",
+                                       "move-cursor",
                                        "(iib)", step, count, TRUE);
 }
 
 static void
-gtk_label_class_init (GtkLabelClass *class)
+gtk_label_set_property (GObject      *object,
+                        guint         prop_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
 {
-  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GtkLabel *self = GTK_LABEL (object);
 
-  gobject_class->set_property = gtk_label_set_property;
-  gobject_class->get_property = gtk_label_get_property;
-  gobject_class->finalize = gtk_label_finalize;
-  gobject_class->dispose = gtk_label_dispose;
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      gtk_label_set_label (self, g_value_get_string (value));
+      break;
+    case PROP_ATTRIBUTES:
+      gtk_label_set_attributes (self, g_value_get_boxed (value));
+      break;
+    case PROP_USE_MARKUP:
+      gtk_label_set_use_markup (self, g_value_get_boolean (value));
+      break;
+    case PROP_USE_UNDERLINE:
+      gtk_label_set_use_underline (self, g_value_get_boolean (value));
+      break;
+    case PROP_JUSTIFY:
+      gtk_label_set_justify (self, g_value_get_enum (value));
+      break;
+    case PROP_WRAP:
+      gtk_label_set_wrap (self, g_value_get_boolean (value));
+      break;
+    case PROP_WRAP_MODE:
+      gtk_label_set_wrap_mode (self, g_value_get_enum (value));
+      break;
+    case PROP_SELECTABLE:
+      gtk_label_set_selectable (self, g_value_get_boolean (value));
+      break;
+    case PROP_MNEMONIC_WIDGET:
+      gtk_label_set_mnemonic_widget (self, (GtkWidget*) g_value_get_object (value));
+      break;
+    case PROP_ELLIPSIZE:
+      gtk_label_set_ellipsize (self, g_value_get_enum (value));
+      break;
+    case PROP_WIDTH_CHARS:
+      gtk_label_set_width_chars (self, g_value_get_int (value));
+      break;
+    case PROP_SINGLE_LINE_MODE:
+      gtk_label_set_single_line_mode (self, g_value_get_boolean (value));
+      break;
+    case PROP_MAX_WIDTH_CHARS:
+      gtk_label_set_max_width_chars (self, g_value_get_int (value));
+      break;
+    case PROP_LINES:
+      gtk_label_set_lines (self, g_value_get_int (value));
+      break;
+    case PROP_XALIGN:
+      gtk_label_set_xalign (self, g_value_get_float (value));
+      break;
+    case PROP_YALIGN:
+      gtk_label_set_yalign (self, g_value_get_float (value));
+      break;
+    case PROP_EXTRA_MENU:
+      gtk_label_set_extra_menu (self, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 
-  widget_class->size_allocate = gtk_label_size_allocate;
-  widget_class->state_flags_changed = gtk_label_state_flags_changed;
-  widget_class->css_changed = gtk_label_css_changed;
-  widget_class->query_tooltip = gtk_label_query_tooltip;
-  widget_class->snapshot = gtk_label_snapshot;
-  widget_class->unrealize = gtk_label_unrealize;
-  widget_class->root = gtk_label_root;
-  widget_class->unroot = gtk_label_unroot;
-  widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
-  widget_class->grab_focus = gtk_label_grab_focus;
-  widget_class->focus = gtk_label_focus;
-  widget_class->get_request_mode = gtk_label_get_request_mode;
-  widget_class->measure = gtk_label_measure;
+static void
+gtk_label_get_property (GObject     *object,
+                        guint        prop_id,
+                        GValue      *value,
+                        GParamSpec  *pspec)
+{
+  GtkLabel *self = GTK_LABEL (object);
 
-  class->move_cursor = gtk_label_move_cursor;
-  class->copy_clipboard = gtk_label_copy_clipboard;
-  class->activate_link = gtk_label_activate_link;
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      g_value_set_string (value, self->label);
+      break;
+    case PROP_ATTRIBUTES:
+      g_value_set_boxed (value, self->attrs);
+      break;
+    case PROP_USE_MARKUP:
+      g_value_set_boolean (value, self->use_markup);
+      break;
+    case PROP_USE_UNDERLINE:
+      g_value_set_boolean (value, self->use_underline);
+      break;
+    case PROP_JUSTIFY:
+      g_value_set_enum (value, self->jtype);
+      break;
+    case PROP_WRAP:
+      g_value_set_boolean (value, self->wrap);
+      break;
+    case PROP_WRAP_MODE:
+      g_value_set_enum (value, self->wrap_mode);
+      break;
+    case PROP_SELECTABLE:
+      g_value_set_boolean (value, gtk_label_get_selectable (self));
+      break;
+    case PROP_MNEMONIC_KEYVAL:
+      g_value_set_uint (value, self->mnemonic_keyval);
+      break;
+    case PROP_MNEMONIC_WIDGET:
+      g_value_set_object (value, (GObject*) self->mnemonic_widget);
+      break;
+    case PROP_ELLIPSIZE:
+      g_value_set_enum (value, self->ellipsize);
+      break;
+    case PROP_WIDTH_CHARS:
+      g_value_set_int (value, gtk_label_get_width_chars (self));
+      break;
+    case PROP_SINGLE_LINE_MODE:
+      g_value_set_boolean (value, gtk_label_get_single_line_mode (self));
+      break;
+    case PROP_MAX_WIDTH_CHARS:
+      g_value_set_int (value, gtk_label_get_max_width_chars (self));
+      break;
+    case PROP_LINES:
+      g_value_set_int (value, gtk_label_get_lines (self));
+      break;
+    case PROP_XALIGN:
+      g_value_set_float (value, gtk_label_get_xalign (self));
+      break;
+    case PROP_YALIGN:
+      g_value_set_float (value, gtk_label_get_yalign (self));
+      break;
+    case PROP_EXTRA_MENU:
+      g_value_set_object (value, gtk_label_get_extra_menu (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 
-  /**
-   * GtkLabel::move-cursor:
-   * @entry: the object which received the signal
-   * @step: the granularity of the move, as a #GtkMovementStep
-   * @count: the number of @step units to move
-   * @extend_selection: %TRUE if the move should extend the selection
-   *
-   * The ::move-cursor signal is a
-   * [keybinding signal][GtkSignalAction]
-   * which gets emitted when the user initiates a cursor movement.
-   * If the cursor is not visible in @entry, this signal causes
-   * the viewport to be moved instead.
-   *
-   * Applications should not connect to it, but may emit it with
-   * g_signal_emit_by_name() if they need to control the cursor
-   * programmatically.
-   *
-   * The default bindings for this signal come in two variants,
-   * the variant with the Shift modifier extends the selection,
-   * the variant without the Shift modifier does not.
-   * There are too many key combinations to list them all here.
-   * - Arrow keys move by individual characters/lines
-   * - Ctrl-arrow key combinations move by words/paragraphs
-   * - Home/End keys move to the ends of the buffer
-   */
-  signals[MOVE_CURSOR] = 
-    g_signal_new (I_("move-cursor"),
-                 G_OBJECT_CLASS_TYPE (gobject_class),
-                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                 G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
-                 NULL, NULL,
-                 _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
-                 G_TYPE_NONE, 3,
-                 GTK_TYPE_MOVEMENT_STEP,
-                 G_TYPE_INT,
-                 G_TYPE_BOOLEAN);
+static void
+gtk_label_init (GtkLabel *self)
+{
+  self->width_chars = -1;
+  self->max_width_chars = -1;
+  self->label = g_strdup ("");
+  self->lines = -1;
 
-   /**
-   * GtkLabel::copy-clipboard:
-   * @self: the object which received the signal
-   *
-   * The ::copy-clipboard signal is a
-   * [keybinding signal][GtkSignalAction]
-   * which gets emitted to copy the selection to the clipboard.
-   *
-   * The default binding for this signal is Ctrl-c.
-   */ 
-  signals[COPY_CLIPBOARD] =
-    g_signal_new (I_("copy-clipboard"),
-                 G_OBJECT_CLASS_TYPE (gobject_class),
-                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                 G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
-                 NULL, NULL,
-                 NULL,
-                 G_TYPE_NONE, 0);
-  
-    /**
-     * GtkLabel::activate-current-link:
-     * @self: The label on which the signal was emitted
-     *
-     * A [keybinding signal][GtkSignalAction]
-     * which gets emitted when the user activates a link in the label.
-     *
-     * 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.
-     */
-    signals[ACTIVATE_CURRENT_LINK] =
-      g_signal_new_class_handler (I_("activate-current-link"),
-                                  G_TYPE_FROM_CLASS (gobject_class),
-                                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                  G_CALLBACK (gtk_label_activate_current_link),
-                                  NULL, NULL,
-                                  NULL,
-                                  G_TYPE_NONE, 0);
+  self->xalign = 0.5;
+  self->yalign = 0.5;
 
-    /**
-     * GtkLabel::activate-link:
-     * @self: The label on which the signal was emitted
-     * @uri: the URI that is activated
-     *
-     * The signal which gets emitted to activate a URI.
-     * Applications may connect to it to override the default behaviour,
-     * which is to call gtk_show_uri().
-     *
-     * Returns: %TRUE if the link has been activated
-     */
-    signals[ACTIVATE_LINK] =
-      g_signal_new (I_("activate-link"),
-                    G_TYPE_FROM_CLASS (gobject_class),
-                    G_SIGNAL_RUN_LAST,
-                    G_STRUCT_OFFSET (GtkLabelClass, activate_link),
-                    _gtk_boolean_handled_accumulator, NULL,
-                    _gtk_marshal_BOOLEAN__STRING,
-                    G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+  self->jtype = GTK_JUSTIFY_LEFT;
+  self->wrap = FALSE;
+  self->wrap_mode = PANGO_WRAP_WORD;
+  self->ellipsize = PANGO_ELLIPSIZE_NONE;
 
-  /**
-   * GtkLabel:label:
-   *
-   * The contents of the label.
-   *
-   * If the string contains [Pango XML markup][PangoMarkupFormat], you will
-   * have to set the #GtkLabel:use-markup property to %TRUE in order for the
-   * label to display the markup attributes. See also gtk_label_set_markup()
-   * for a convenience function that sets both this property and the
-   * #GtkLabel:use-markup property at the same time.
-   *
-   * If the string contains underlines acting as mnemonics, you will have to
-   * set the #GtkLabel:use-underline property to %TRUE in order for the label
-   * to display them.
-   */
-  label_props[PROP_LABEL] =
-      g_param_spec_string ("label",
-                           P_("Label"),
-                           P_("The text of the label"),
-                           "",
-                           GTK_PARAM_READWRITE);
+  self->use_underline = FALSE;
+  self->use_markup = FALSE;
 
-  label_props[PROP_ATTRIBUTES] =
-      g_param_spec_boxed ("attributes",
-                          P_("Attributes"),
-                          P_("A list of style attributes to apply to the text of the label"),
-                          PANGO_TYPE_ATTR_LIST,
-                          GTK_PARAM_READWRITE);
+  self->mnemonic_keyval = GDK_KEY_VoidSymbol;
+  self->layout = NULL;
+  self->text = g_strdup ("");
+  self->attrs = NULL;
 
-  label_props[PROP_USE_MARKUP] =
-      g_param_spec_boolean ("use-markup",
-                            P_("Use markup"),
-                            P_("The text of the label includes XML markup. See pango_parse_markup()"),
-                            FALSE,
-                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  self->mnemonic_widget = NULL;
 
-  label_props[PROP_USE_UNDERLINE] =
-      g_param_spec_boolean ("use-underline",
-                            P_("Use underline"),
-                            P_("If set, an underline in the text indicates the next character should be used 
for the mnemonic accelerator key"),
-                            FALSE,
-                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  self->mnemonics_visible = FALSE;
+}
 
-  label_props[PROP_JUSTIFY] =
-      g_param_spec_enum ("justify",
-                         P_("Justification"),
-                         P_("The alignment of the lines in the text of the label relative to each other. 
This does NOT affect the alignment of the label within its allocation. See GtkLabel:xalign for that"),
-                         GTK_TYPE_JUSTIFICATION,
-                         GTK_JUSTIFY_LEFT,
-                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static const GtkBuildableParser pango_parser =
+{
+  gtk_pango_attribute_start_element,
+};
 
-  /**
-   * GtkLabel:xalign:
-   *
-   * The xalign property determines the horizontal alignment of the label text
-   * inside the labels size allocation. Compare this to #GtkWidget:halign,
-   * which determines how the labels size allocation is positioned in the
-   * space available for the label.
-   */
-  label_props[PROP_XALIGN] =
-      g_param_spec_float ("xalign",
-                          P_("X align"),
-                          P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL 
layouts."),
-                          0.0, 1.0,
-                          0.5,
-                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static gboolean
+gtk_label_buildable_custom_tag_start (GtkBuildable       *buildable,
+                                      GtkBuilder         *builder,
+                                      GObject            *child,
+                                      const char         *tagname,
+                                      GtkBuildableParser *parser,
+                                      gpointer           *data)
+{
+  if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
+                                                tagname, parser, data))
+    return TRUE;
 
-  /**
-   * GtkLabel:yalign:
-   *
-   * The yalign property determines the vertical alignment of the label text
-   * inside the labels size allocation. Compare this to #GtkWidget:valign,
-   * which determines how the labels size allocation is positioned in the
-   * space available for the label.
-   */
-  label_props[PROP_YALIGN] =
-      g_param_spec_float ("yalign",
-                          P_("Y align"),
-                          P_("The vertical alignment, from 0 (top) to 1 (bottom)"),
-                          0.0, 1.0,
-                          0.5,
-                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  if (strcmp (tagname, "attributes") == 0)
+    {
+      GtkPangoAttributeParserData *parser_data;
 
-  label_props[PROP_WRAP] =
-      g_param_spec_boolean ("wrap",
-                            P_("Line wrap"),
-                            P_("If set, wrap lines if the text becomes too wide"),
-                            FALSE,
-                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+      parser_data = g_slice_new0 (GtkPangoAttributeParserData);
+      parser_data->builder = g_object_ref (builder);
+      parser_data->object = (GObject *) g_object_ref (buildable);
+      *parser = pango_parser;
+      *data = parser_data;
+      return TRUE;
+    }
+  return FALSE;
+}
 
-  /**
-   * GtkLabel:wrap-mode:
-   *
-   * If line wrapping is on (see the #GtkLabel:wrap property) this controls
-   * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
-   * means wrap on word boundaries.
-   */
-  label_props[PROP_WRAP_MODE] =
-      g_param_spec_enum ("wrap-mode",
-                         P_("Line wrap mode"),
-                         P_("If wrap is set, controls how linewrapping is done"),
-                         PANGO_TYPE_WRAP_MODE,
-                         PANGO_WRAP_WORD,
-                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static void
+gtk_label_buildable_custom_finished (GtkBuildable *buildable,
+                                     GtkBuilder   *builder,
+                                     GObject      *child,
+                                     const char   *tagname,
+                                     gpointer      user_data)
+{
+  GtkPangoAttributeParserData *data = user_data;
 
-  label_props[PROP_SELECTABLE] =
-      g_param_spec_boolean ("selectable",
-                            P_("Selectable"),
-                            P_("Whether the label text can be selected with the mouse"),
-                            FALSE,
-                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  buildable_parent_iface->custom_finished (buildable, builder, child,
+                                           tagname, user_data);
 
-  label_props[PROP_MNEMONIC_KEYVAL] =
-      g_param_spec_uint ("mnemonic-keyval",
-                         P_("Mnemonic key"),
-                         P_("The mnemonic accelerator key for this label"),
-                         0, G_MAXUINT,
-                         GDK_KEY_VoidSymbol,
-                         GTK_PARAM_READABLE);
+  if (strcmp (tagname, "attributes") == 0)
+    {
+      if (data->attrs)
+        {
+          gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
+          pango_attr_list_unref (data->attrs);
+        }
 
-  label_props[PROP_MNEMONIC_WIDGET] =
-      g_param_spec_object ("mnemonic-widget",
-                           P_("Mnemonic widget"),
-                           P_("The widget to be activated when the label’s mnemonic key is pressed"),
-                           GTK_TYPE_WIDGET,
-                           GTK_PARAM_READWRITE);
+      g_object_unref (data->object);
+      g_object_unref (data->builder);
+      g_slice_free (GtkPangoAttributeParserData, data);
+    }
+}
 
-  /**
-   * GtkLabel:ellipsize:
-   *
-   * The preferred place to ellipsize the string, if the label does
-   * not have enough room to display the entire string, specified as a
-   * #PangoEllipsizeMode.
-   *
-   * Note that setting this property to a value other than
-   * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
-   * only enough space to display the ellipsis "...". In particular, this
-   * means that ellipsizing labels do not work well in notebook tabs, unless
-   * the #GtkNotebook tab-expand child property is set to %TRUE. Other ways
-   * to set a label's width are gtk_widget_set_size_request() and
-   * gtk_label_set_width_chars().
-   */
-  label_props[PROP_ELLIPSIZE] =
-      g_param_spec_enum ("ellipsize",
-                         P_("Ellipsize"),
-                         P_("The preferred place to ellipsize the string, if the label does not have enough 
room to display the entire string"),
-                         PANGO_TYPE_ELLIPSIZE_MODE,
-                         PANGO_ELLIPSIZE_NONE,
-                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static void
+gtk_label_buildable_interface_init (GtkBuildableIface *iface)
+{
+  buildable_parent_iface = g_type_interface_peek_parent (iface);
 
-  /**
-   * GtkLabel:width-chars:
-   *
-   * The desired width of the label, in characters. If this property is set to
-   * -1, the width will be calculated automatically.
-   *
-   * See the section on [text layout][label-text-layout]
-   * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
-   * determine the width of ellipsized and wrapped labels.
-   **/
-  label_props[PROP_WIDTH_CHARS] =
-      g_param_spec_int ("width-chars",
-                        P_("Width In Characters"),
-                        P_("The desired width of the label, in characters"),
-                        -1, G_MAXINT,
-                        -1,
-                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
+  iface->custom_finished = gtk_label_buildable_custom_finished;
+}
 
-  /**
-   * GtkLabel:single-line-mode:
-   *
-   * Whether the label is in single line mode. In single line mode,
-   * the height of the label does not depend on the actual text, it
-   * is always set to ascent + descent of the font. This can be an
-   * advantage in situations where resizing the label because of text
-   * changes would be distracting, e.g. in a statusbar.
-   **/
-  label_props[PROP_SINGLE_LINE_MODE] =
-      g_param_spec_boolean ("single-line-mode",
-                            P_("Single Line Mode"),
-                            P_("Whether the label is in single line mode"),
-                            FALSE,
-                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+static void
+update_link_state (GtkLabel *self)
+{
+  GtkStateFlags state;
+  guint i;
 
-  /**
-   * GtkLabel:max-width-chars:
-   *
-   * The desired maximum width of the label, in characters. If this property
-   * is set to -1, the width will be calculated automatically.
-   *
-   * See the section on [text layout][label-text-layout]
-   * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
-   * determine the width of ellipsized and wrapped labels.
-   **/
-  label_props[PROP_MAX_WIDTH_CHARS] =
-      g_param_spec_int ("max-width-chars",
-                        P_("Maximum Width In Characters"),
-                        P_("The desired maximum width of the label, in characters"),
-                        -1, G_MAXINT,
-                        -1,
-                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  if (!self->select_info)
+    return;
 
-  /**
-   * GtkLabel:lines:
-   *
-   * The number of lines to which an ellipsized, wrapping label
-   * should be limited. This property has no effect if the
-   * label is not wrapping or ellipsized. Set this property to
-   * -1 if you don't want to limit the number of lines.
-   */
-  label_props[PROP_LINES] =
-      g_param_spec_int ("lines",
-                        P_("Number of lines"),
-                        P_("The desired number of lines, when ellipsizing a wrapping label"),
-                        -1, G_MAXINT,
-                        -1,
-                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  for (i = 0; i < self->select_info->n_links; i++)
+    {
+      const GtkLabelLink *link = &self->select_info->links[i];
 
-  /**
-   * GtkLabel:extra-menu:
-   *
-   * A menu model whose contents will be appended to
-   * the context menu.
-   */
-  label_props[PROP_EXTRA_MENU] =
-      g_param_spec_object ("extra-menu",
-                          P_("Extra menu"),
-                          P_("Menu model to append to the context menu"),
-                          G_TYPE_MENU_MODEL,
-                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+      state = gtk_widget_get_state_flags (GTK_WIDGET (self));
+      if (link->visited)
+        state |= GTK_STATE_FLAG_VISITED;
+      else
+        state |= GTK_STATE_FLAG_LINK;
+      if (link == self->select_info->active_link)
+        {
+          if (self->select_info->link_clicked)
+            state |= GTK_STATE_FLAG_ACTIVE;
+          else
+            state |= GTK_STATE_FLAG_PRELIGHT;
+        }
+      gtk_css_node_set_state (link->cssnode, state);
+    }
+}
 
-  g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props);
+static void
+gtk_label_update_cursor (GtkLabel *self)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
 
-  /**
-   * GtkLabel|menu.popup:
-   *
-   * Opens the context menu. 
-   */
-  gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_label_popup_menu);
+  if (!self->select_info)
+    return;
 
-  /*
-   * Key bindings
-   */
+  if (gtk_widget_is_sensitive (widget))
+    {
+      if (self->select_info->active_link)
+        gtk_widget_set_cursor_from_name (widget, "pointer");
+      else if (self->select_info->selectable)
+        gtk_widget_set_cursor_from_name (widget, "text");
+      else
+        gtk_widget_set_cursor (widget, NULL);
+    }
+  else
+    gtk_widget_set_cursor (widget, NULL);
+}
 
-  gtk_widget_class_add_binding_action (widget_class,
-                                       GDK_KEY_F10, GDK_SHIFT_MASK,
-                                       "menu.popup",
-                                       NULL);
-  gtk_widget_class_add_binding_action (widget_class,
-                                       GDK_KEY_Menu, 0,
-                                       "menu.popup",
-                                       NULL);
+static void
+gtk_label_state_flags_changed (GtkWidget     *widget,
+                               GtkStateFlags  prev_state)
+{
+  GtkLabel *self = GTK_LABEL (widget);
 
-  /* Moving the insertion point */
-  add_move_binding (widget_class, GDK_KEY_Right, 0,
-                   GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+  if (self->select_info)
+    {
+      if (!gtk_widget_is_sensitive (widget))
+        gtk_label_select_region (self, 0, 0);
 
-  add_move_binding (widget_class, GDK_KEY_Left, 0,
-                   GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+      gtk_label_update_cursor (self);
+      update_link_state (self);
+    }
 
-  add_move_binding (widget_class, GDK_KEY_KP_Right, 0,
-                   GTK_MOVEMENT_VISUAL_POSITIONS, 1);
-  
-  add_move_binding (widget_class, GDK_KEY_KP_Left, 0,
-                   GTK_MOVEMENT_VISUAL_POSITIONS, -1);
-  
-  add_move_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
-  
-  add_move_binding (widget_class, GDK_KEY_b, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
-  
-  add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_WORDS, 1);
+  if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
+    GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
+}
 
-  add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_WORDS, -1);
+static void
+gtk_label_update_layout_attributes (GtkLabel      *self,
+                                    PangoAttrList *style_attrs)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+  GtkCssStyle *style;
+  PangoAttrList *attrs;
 
-  add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_WORDS, 1);
+  if (self->layout == NULL)
+    {
+      pango_attr_list_unref (style_attrs);
+      return;
+    }
 
-  add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_WORDS, -1);
+  if (self->select_info && self->select_info->links)
+    {
+      guint i;
 
-  /* select all */
-  gtk_widget_class_add_binding (widget_class,
-                                GDK_KEY_a, GDK_CONTROL_MASK,
-                                (GtkShortcutFunc) gtk_label_select_all,
-                                NULL);
-  gtk_widget_class_add_binding (widget_class,
-                                GDK_KEY_slash, GDK_CONTROL_MASK,
-                                (GtkShortcutFunc) gtk_label_select_all,
-                                NULL);
+      attrs = pango_attr_list_new ();
 
-  /* unselect all */
-  gtk_widget_class_add_binding_signal (widget_class,
-                                       GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
-                                      "move-cursor",
-                                       "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
+      for (i = 0; i < self->select_info->n_links; i++)
+        {
+          const GtkLabelLink *link = &self->select_info->links[i];
+          const GdkRGBA *link_color;
+          PangoAttrList *link_attrs;
+          PangoAttribute *attr;
 
-  gtk_widget_class_add_binding_signal (widget_class,
-                                       GDK_KEY_backslash, GDK_CONTROL_MASK,
-                                      "move-cursor",
-                                       "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
+          style = gtk_css_node_get_style (link->cssnode);
+          link_attrs = gtk_css_style_get_pango_attributes (style);
+          if (link_attrs)
+            {
+              GSList *attributes = pango_attr_list_get_attributes (link_attrs);
+              GSList *l;
+              for (l = attributes; l; l = l->next)
+                {
+                  attr = l->data;
 
-  add_move_binding (widget_class, GDK_KEY_f, GDK_ALT_MASK,
-                   GTK_MOVEMENT_WORDS, 1);
+                  attr->start_index = link->start;
+                  attr->end_index = link->end;
+                  pango_attr_list_insert (attrs, attr);
+                }
+              g_slist_free (attributes);
+            }
 
-  add_move_binding (widget_class, GDK_KEY_b, GDK_ALT_MASK,
-                   GTK_MOVEMENT_WORDS, -1);
+          link_color = gtk_css_color_value_get_rgba (style->core->color);
+          attr = pango_attr_foreground_new (link_color->red * 65535,
+                                            link_color->green * 65535,
+                                            link_color->blue * 65535);
+          attr->start_index = link->start;
+          attr->end_index = link->end;
+          pango_attr_list_insert (attrs, attr);
 
-  add_move_binding (widget_class, GDK_KEY_Home, 0,
-                   GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+          pango_attr_list_unref (link_attrs);
+        }
+    }
+  else
+    attrs = NULL;
 
-  add_move_binding (widget_class, GDK_KEY_End, 0,
-                   GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+  style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
+  if (!style_attrs)
+    style_attrs = gtk_css_style_get_pango_attributes (style);
 
-  add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
-                   GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+  if (style_attrs)
+    {
+      attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
+      pango_attr_list_unref (style_attrs);
+    }
 
-  add_move_binding (widget_class, GDK_KEY_KP_End, 0,
-                   GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
-  
-  add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_BUFFER_ENDS, -1);
+  attrs = _gtk_pango_attr_list_merge (attrs, self->markup_attrs);
+  attrs = _gtk_pango_attr_list_merge (attrs, self->attrs);
 
-  add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_BUFFER_ENDS, 1);
+  pango_layout_set_attributes (self->layout, attrs);
 
-  add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_BUFFER_ENDS, -1);
+  pango_attr_list_unref (attrs);
+}
 
-  add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK,
-                   GTK_MOVEMENT_BUFFER_ENDS, 1);
+static void
+gtk_label_css_changed (GtkWidget         *widget,
+                       GtkCssStyleChange *change)
+{
+  GtkLabel *self = GTK_LABEL (widget);
+  gboolean attrs_affected;
+  PangoAttrList *new_attrs = NULL;
 
-  /* copy */
-  gtk_widget_class_add_binding_signal (widget_class,
-                                       GDK_KEY_c, GDK_CONTROL_MASK,
-                                      "copy-clipboard",
-                                       NULL);
+  GTK_WIDGET_CLASS (gtk_label_parent_class)->css_changed (widget, change);
 
-  gtk_widget_class_add_binding_signal (widget_class,
-                                       GDK_KEY_Return, 0,
-                                      "activate-current-link",
-                                       NULL);
-  gtk_widget_class_add_binding_signal (widget_class,
-                                       GDK_KEY_ISO_Enter, 0,
-                                      "activate-current-link",
-                                       NULL);
-  gtk_widget_class_add_binding_signal (widget_class,
-                                       GDK_KEY_KP_Enter, 0,
-                                      "activate-current-link",
-                                       NULL);
+  if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS))
+    {
+      new_attrs = gtk_css_style_get_pango_attributes (gtk_css_style_change_get_new_style (change));
+      attrs_affected = (self->layout && pango_layout_get_attributes (self->layout)) ||
+                       new_attrs;
+    }
+  else
+    attrs_affected = FALSE;
 
-  gtk_widget_class_set_css_name (widget_class, I_("label"));
-  gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
+  if (change == NULL || attrs_affected  || (self->select_info && self->select_info->links))
+    {
+      gtk_label_update_layout_attributes (self, new_attrs);
 
-  quark_mnemonics_visible_connected = g_quark_from_static_string ("gtk-label-mnemonics-visible-connected");
+      if (attrs_affected)
+        gtk_widget_queue_draw (widget);
+    }
+}
 
-  /**
-   * GtkLabel|clipboard.cut:
-   *
-   * Doesn't do anything, since text in labels can't be deleted.
-   */
-  gtk_widget_class_install_action (widget_class, "clipboard.cut", NULL,
-                                   gtk_label_nop);
+static PangoDirection
+get_cursor_direction (GtkLabel *self)
+{
+  GSList *l;
 
-  /**
-   * GtkLabel|clipboard.copy:
-   *
-   * Copies the text to the clipboard.
-   */
-  gtk_widget_class_install_action (widget_class, "clipboard.copy", NULL,
-                                   gtk_label_activate_clipboard_copy);
+  g_assert (self->select_info);
 
-  /**
-   * GtkLabel|clipboard.paste:
-   *
-   * Doesn't do anything, since text in labels can't be edited.
-   */
-  gtk_widget_class_install_action (widget_class, "clipboard.paste", NULL,
-                                   gtk_label_nop);
+  gtk_label_ensure_layout (self);
 
-  /**
-   * GtkLabel|selection.delete:
-   *
-   * Doesn't do anything, since text in labels can't be deleted.
-   */
-  gtk_widget_class_install_action (widget_class, "selection.delete", NULL,
-                                   gtk_label_nop);
+  for (l = pango_layout_get_lines_readonly (self->layout); l; l = l->next)
+    {
+      PangoLayoutLine *line = l->data;
 
-  /**
-   * GtkLabel|selection.select-all:
-   *
-   * Selects all of the text, if the label allows selection.
-   */
-  gtk_widget_class_install_action (widget_class, "selection.select-all", NULL,
-                                   gtk_label_activate_selection_select_all);
-
-  /**
-   * GtkLabel|link.open:
-   *
-   * Opens the link, when activated on a link inside the label.
-   */
-  gtk_widget_class_install_action (widget_class, "link.open", NULL,
-                                   gtk_label_activate_link_open);
+      /* If self->select_info->selection_end is at the very end of
+       * the line, we don't know if the cursor is on this line or
+       * the next without looking ahead at the next line. (End
+       * of paragraph is different from line break.) But it's
+       * definitely in this paragraph, which is good enough
+       * to figure out the resolved direction.
+       */
+       if (line->start_index + line->length >= self->select_info->selection_end)
+        return line->resolved_dir;
+    }
 
-  /**
-   * GtkLabel|link.copy:
-   *
-   * Copies the link to the clipboard, when activated on a link
-   * inside the label.
-   */
-  gtk_widget_class_install_action (widget_class, "link.copy", NULL,
-                                   gtk_label_activate_link_copy);
+  return PANGO_DIRECTION_LTR;
 }
 
-static void 
-gtk_label_set_property (GObject      *object,
-                       guint         prop_id,
-                       const GValue *value,
-                       GParamSpec   *pspec)
+static GtkLabelLink *
+gtk_label_get_focus_link (GtkLabel *self,
+                          int      *out_index)
 {
-  GtkLabel *self = GTK_LABEL (object);
+  GtkLabelSelectionInfo *info = self->select_info;
+  int link_index;
 
-  switch (prop_id)
+  if (!info ||
+      info->selection_anchor != info->selection_end)
+    goto nope;
+
+  link_index = _gtk_label_get_link_at (self, info->selection_anchor);
+
+  if (link_index != -1)
     {
-    case PROP_LABEL:
-      gtk_label_set_label (self, g_value_get_string (value));
-      break;
-    case PROP_ATTRIBUTES:
-      gtk_label_set_attributes (self, g_value_get_boxed (value));
-      break;
-    case PROP_USE_MARKUP:
-      gtk_label_set_use_markup (self, g_value_get_boolean (value));
-      break;
-    case PROP_USE_UNDERLINE:
-      gtk_label_set_use_underline (self, g_value_get_boolean (value));
-      break;
-    case PROP_JUSTIFY:
-      gtk_label_set_justify (self, g_value_get_enum (value));
-      break;
-    case PROP_WRAP:
-      gtk_label_set_wrap (self, g_value_get_boolean (value));
-      break;
-    case PROP_WRAP_MODE:
-      gtk_label_set_wrap_mode (self, g_value_get_enum (value));
-      break;
-    case PROP_SELECTABLE:
-      gtk_label_set_selectable (self, g_value_get_boolean (value));
-      break;
-    case PROP_MNEMONIC_WIDGET:
-      gtk_label_set_mnemonic_widget (self, (GtkWidget*) g_value_get_object (value));
-      break;
-    case PROP_ELLIPSIZE:
-      gtk_label_set_ellipsize (self, g_value_get_enum (value));
-      break;
-    case PROP_WIDTH_CHARS:
-      gtk_label_set_width_chars (self, g_value_get_int (value));
-      break;
-    case PROP_SINGLE_LINE_MODE:
-      gtk_label_set_single_line_mode (self, g_value_get_boolean (value));
-      break;
-    case PROP_MAX_WIDTH_CHARS:
-      gtk_label_set_max_width_chars (self, g_value_get_int (value));
-      break;
-    case PROP_LINES:
-      gtk_label_set_lines (self, g_value_get_int (value));
-      break;
-    case PROP_XALIGN:
-      gtk_label_set_xalign (self, g_value_get_float (value));
-      break;
-    case PROP_YALIGN:
-      gtk_label_set_yalign (self, g_value_get_float (value));
-      break;
-    case PROP_EXTRA_MENU:
-      gtk_label_set_extra_menu (self, g_value_get_object (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+      if (out_index)
+        *out_index = link_index;
+
+      return &info->links[link_index];
     }
+
+nope:
+  if (out_index)
+    *out_index = -1;
+  return NULL;
 }
 
-static void 
-gtk_label_get_property (GObject     *object,
-                       guint        prop_id,
-                       GValue      *value,
-                       GParamSpec  *pspec)
+/**
+ * gtk_label_get_measuring_layout:
+ * @self: the label
+ * @existing_layout: %NULL or an existing layout already in use.
+ * @width: the width to measure with in pango units, or -1 for infinite
+ *
+ * Gets a layout that can be used for measuring sizes. The returned
+ * layout will be identical to the label’s layout except for the
+ * layout’s width, which will be set to @width. Do not modify the returned
+ * layout.
+ *
+ * Returns: a new reference to a pango layout
+ **/
+static PangoLayout *
+gtk_label_get_measuring_layout (GtkLabel    *self,
+                                PangoLayout *existing_layout,
+                                int          width)
 {
-  GtkLabel *self = GTK_LABEL (object);
+  PangoLayout *copy;
 
-  switch (prop_id)
+  if (existing_layout != NULL)
     {
-    case PROP_LABEL:
-      g_value_set_string (value, self->label);
-      break;
-    case PROP_ATTRIBUTES:
-      g_value_set_boxed (value, self->attrs);
-      break;
-    case PROP_USE_MARKUP:
-      g_value_set_boolean (value, self->use_markup);
-      break;
-    case PROP_USE_UNDERLINE:
-      g_value_set_boolean (value, self->use_underline);
-      break;
-    case PROP_JUSTIFY:
-      g_value_set_enum (value, self->jtype);
-      break;
-    case PROP_WRAP:
-      g_value_set_boolean (value, self->wrap);
-      break;
-    case PROP_WRAP_MODE:
-      g_value_set_enum (value, self->wrap_mode);
-      break;
-    case PROP_SELECTABLE:
-      g_value_set_boolean (value, gtk_label_get_selectable (self));
-      break;
-    case PROP_MNEMONIC_KEYVAL:
-      g_value_set_uint (value, self->mnemonic_keyval);
-      break;
-    case PROP_MNEMONIC_WIDGET:
-      g_value_set_object (value, (GObject*) self->mnemonic_widget);
-      break;
-    case PROP_ELLIPSIZE:
-      g_value_set_enum (value, self->ellipsize);
-      break;
-    case PROP_WIDTH_CHARS:
-      g_value_set_int (value, gtk_label_get_width_chars (self));
-      break;
-    case PROP_SINGLE_LINE_MODE:
-      g_value_set_boolean (value, gtk_label_get_single_line_mode (self));
-      break;
-    case PROP_MAX_WIDTH_CHARS:
-      g_value_set_int (value, gtk_label_get_max_width_chars (self));
-      break;
-    case PROP_LINES:
-      g_value_set_int (value, gtk_label_get_lines (self));
-      break;
-    case PROP_XALIGN:
-      g_value_set_float (value, gtk_label_get_xalign (self));
-      break;
-    case PROP_YALIGN:
-      g_value_set_float (value, gtk_label_get_yalign (self));
-      break;
-    case PROP_EXTRA_MENU:
-      g_value_set_object (value, gtk_label_get_extra_menu (self));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+      if (existing_layout != self->layout)
+        {
+          pango_layout_set_width (existing_layout, width);
+          return existing_layout;
+        }
+
+      g_object_unref (existing_layout);
     }
-}
 
-static void
-gtk_label_init (GtkLabel *self)
-{
-  self->width_chars = -1;
-  self->max_width_chars = -1;
-  self->label = g_strdup ("");
-  self->lines = -1;
+  gtk_label_ensure_layout (self);
 
-  self->xalign = 0.5;
-  self->yalign = 0.5;
+  if (pango_layout_get_width (self->layout) == width)
+    {
+      g_object_ref (self->layout);
+      return self->layout;
+    }
 
-  self->jtype = GTK_JUSTIFY_LEFT;
-  self->wrap = FALSE;
-  self->wrap_mode = PANGO_WRAP_WORD;
-  self->ellipsize = PANGO_ELLIPSIZE_NONE;
+  /* 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_width (GTK_WIDGET (self)) <= 1)
+    {
+      g_object_ref (self->layout);
+      pango_layout_set_width (self->layout, width);
+      return self->layout;
+    }
 
-  self->use_underline = FALSE;
-  self->use_markup = FALSE;
+  /* oftentimes we want to measure a width that is far wider than the current width,
+   * even though the layout would not change if we made it wider. In that case, we
+   * can just return the current layout, because for measuring purposes, it will be
+   * identical.
+   */
+  if (!pango_layout_is_wrapped (self->layout) &&
+      !pango_layout_is_ellipsized (self->layout))
+    {
+      PangoRectangle rect;
 
-  self->mnemonic_keyval = GDK_KEY_VoidSymbol;
-  self->layout = NULL;
-  self->text = g_strdup ("");
-  self->attrs = NULL;
+      if (width == -1)
+        return g_object_ref (self->layout);
 
-  self->mnemonic_widget = NULL;
+      pango_layout_get_extents (self->layout, NULL, &rect);
+      if (rect.width <= width)
+        return g_object_ref (self->layout);
+    }
 
-  self->mnemonics_visible = FALSE;
+  copy = pango_layout_copy (self->layout);
+  pango_layout_set_width (copy, width);
+  return copy;
 }
 
-
 static void
-gtk_label_buildable_interface_init (GtkBuildableIface *iface)
-{
-  buildable_parent_iface = g_type_interface_peek_parent (iface);
-
-  iface->custom_tag_start = gtk_label_buildable_custom_tag_start;
-  iface->custom_finished = gtk_label_buildable_custom_finished;
-}
-
-static const GtkBuildableParser pango_parser =
-{
-  gtk_pango_attribute_start_element,
-};
-
-static gboolean
-gtk_label_buildable_custom_tag_start (GtkBuildable       *buildable,
-                                      GtkBuilder         *builder,
-                                      GObject            *child,
-                                      const char         *tagname,
-                                      GtkBuildableParser *parser,
-                                      gpointer           *data)
+get_height_for_width (GtkLabel *self,
+                      int       width,
+                      int      *minimum_height,
+                      int      *natural_height,
+                      int      *minimum_baseline,
+                      int      *natural_baseline)
 {
-  if (buildable_parent_iface->custom_tag_start (buildable, builder, child,
-                                               tagname, parser, data))
-    return TRUE;
-
-  if (strcmp (tagname, "attributes") == 0)
-    {
-      GtkPangoAttributeParserData *parser_data;
+  PangoLayout *layout;
+  int text_height, baseline;
 
-      parser_data = g_slice_new0 (GtkPangoAttributeParserData);
-      parser_data->builder = g_object_ref (builder);
-      parser_data->object = (GObject *) g_object_ref (buildable);
-      *parser = pango_parser;
-      *data = parser_data;
-      return TRUE;
-    }
-  return FALSE;
-}
+  layout = gtk_label_get_measuring_layout (self, NULL, width * PANGO_SCALE);
 
-static void
-gtk_label_buildable_custom_finished (GtkBuildable *buildable,
-                                    GtkBuilder   *builder,
-                                    GObject      *child,
-                                    const char   *tagname,
-                                    gpointer      user_data)
-{
-  GtkPangoAttributeParserData *data = user_data;
+  pango_layout_get_pixel_size (layout, NULL, &text_height);
 
-  buildable_parent_iface->custom_finished (buildable, builder, child,
-                                          tagname, user_data);
+  *minimum_height = text_height;
+  *natural_height = text_height;
 
-  if (strcmp (tagname, "attributes") == 0)
-    {
-      if (data->attrs)
-       {
-         gtk_label_set_attributes (GTK_LABEL (buildable), data->attrs);
-         pango_attr_list_unref (data->attrs);
-       }
+  baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+  *minimum_baseline = baseline;
+  *natural_baseline = baseline;
 
-      g_object_unref (data->object);
-      g_object_unref (data->builder);
-      g_slice_free (GtkPangoAttributeParserData, data);
-    }
+  g_object_unref (layout);
 }
 
-
-/**
- * gtk_label_new:
- * @str: (nullable): The text of the label
- *
- * Creates a new label with the given text inside it. You can
- * pass %NULL to get an empty label widget.
- *
- * Returns: the new #GtkLabel
- **/
-GtkWidget*
-gtk_label_new (const char *str)
+static int
+get_char_pixels (GtkWidget   *self,
+                 PangoLayout *layout)
 {
-  GtkLabel *self;
-
-  self = g_object_new (GTK_TYPE_LABEL, NULL);
+  PangoContext *context;
+  PangoFontMetrics *metrics;
+  int char_width, digit_width;
 
-  if (str && *str)
-    gtk_label_set_text (self, str);
+  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 GTK_WIDGET (self);
+  return MAX (char_width, digit_width);;
 }
 
-/**
- * gtk_label_new_with_mnemonic:
- * @str: (nullable): The text of the label, with an underscore in front of the
- *       mnemonic character
- *
- * Creates a new #GtkLabel, containing the text in @str.
- *
- * If characters in @str are preceded by an underscore, they are
- * underlined. If you need a literal underscore character in a label, use
- * '__' (two underscores). The first underlined character represents a 
- * keyboard accelerator called a mnemonic. The mnemonic key can be used 
- * to activate another widget, chosen automatically, or explicitly using
- * gtk_label_set_mnemonic_widget().
- * 
- * If gtk_label_set_mnemonic_widget() is not called, then the first 
- * activatable ancestor of the #GtkLabel will be chosen as the mnemonic 
- * widget. For instance, if the label is inside a button or menu item, 
- * the button or menu item will automatically become the mnemonic widget 
- * and be activated by the mnemonic.
- *
- * Returns: the new #GtkLabel
- **/
-GtkWidget*
-gtk_label_new_with_mnemonic (const char *str)
+static void
+gtk_label_get_preferred_layout_size (GtkLabel *self,
+                                     PangoRectangle *smallest,
+                                     PangoRectangle *widest,
+                                     int *smallest_baseline,
+                                     int *widest_baseline)
 {
-  GtkLabel *self;
-
-  self = g_object_new (GTK_TYPE_LABEL, NULL);
-
-  if (str && *str)
-    gtk_label_set_text_with_mnemonic (self, str);
+  PangoLayout *layout;
+  int char_pixels;
 
-  return GTK_WIDGET (self);
-}
+  /* "width-chars" Hard-coded minimum width:
+   *    - minimum size should be MAX (width-chars, strlen ("..."));
+   *    - natural size should be MAX (width-chars, strlen (self->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 (self->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.
+   */
 
-static gboolean
-gtk_label_mnemonic_activate (GtkWidget *widget,
-                            gboolean   group_cycling)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-  GtkWidget *parent;
+  /* Start off with the pixel extents of an as-wide-as-possible layout */
+  layout = gtk_label_get_measuring_layout (self, NULL, -1);
 
-  if (self->mnemonic_widget)
-    return gtk_widget_mnemonic_activate (self->mnemonic_widget, group_cycling);
+  if (self->width_chars > -1 || self->max_width_chars > -1)
+    char_pixels = get_char_pixels (GTK_WIDGET (self), layout);
+  else
+    char_pixels = 0;
 
-  /* Try to find the widget to activate by traversing the
-   * widget's ancestry.
-   */
-  parent = gtk_widget_get_parent (widget);
+  pango_layout_get_extents (layout, NULL, widest);
+  widest->width = MAX (widest->width, char_pixels * self->width_chars);
+  widest->x = widest->y = 0;
+  *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
 
-  if (GTK_IS_NOTEBOOK (parent))
-    return FALSE;
-  
-  while (parent)
+  if (self->ellipsize || self->wrap)
     {
-      if (gtk_widget_get_can_focus (parent) ||
-         (!group_cycling && gtk_widget_can_activate (parent)) ||
-          GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)))
-       return gtk_widget_mnemonic_activate (parent, group_cycling);
-      parent = gtk_widget_get_parent (parent);
-    }
-
-  /* barf if there was nothing to activate */
-  g_warning ("Couldn't find a target for a mnemonic activation.");
-  gtk_widget_error_bell (widget);
+      /* a layout with width 0 will be as small as humanly possible */
+      layout = gtk_label_get_measuring_layout (self,
+                                               layout,
+                                               self->width_chars > -1 ? char_pixels * self->width_chars
+                                                                      : 0);
 
-  return FALSE;
-}
+      pango_layout_get_extents (layout, NULL, smallest);
+      smallest->width = MAX (smallest->width, char_pixels * self->width_chars);
+      smallest->x = smallest->y = 0;
 
-static void
-label_mnemonics_visible_changed (GtkWidget  *widget,
-                                 GParamSpec *pspec,
-                                 gpointer    data)
-{
-  gboolean visible;
+      *smallest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
 
-  g_object_get (widget, "mnemonics-visible", &visible, NULL);
-  _gtk_label_mnemonics_visible_apply_recursively (widget, visible);
-}
+      if (self->max_width_chars > -1 && widest->width > char_pixels * self->max_width_chars)
+        {
+          layout = gtk_label_get_measuring_layout (self,
+                                                   layout,
+                                                   MAX (smallest->width, char_pixels * 
self->max_width_chars));
+          pango_layout_get_extents (layout, NULL, widest);
+          widest->width = MAX (widest->width, char_pixels * self->width_chars);
+          widest->x = widest->y = 0;
 
-static void
-gtk_label_setup_mnemonic (GtkLabel *self)
-{
-  GtkWidget *widget = GTK_WIDGET (self);
-  GtkShortcut *shortcut;
-  GtkNative *native;
-  gboolean connected;
-  gboolean mnemonics_visible;
+          *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+        }
 
-  if (self->mnemonic_keyval == GDK_KEY_VoidSymbol)
-    {
-      if (self->mnemonic_controller)
+      if (widest->width < smallest->width)
         {
-          gtk_widget_remove_controller (widget, self->mnemonic_controller);
-          self->mnemonic_controller = NULL;
+          *smallest = *widest;
+          *smallest_baseline = *widest_baseline;
         }
-      return;
-    }
-
-  if (self->mnemonic_controller == NULL)
-    {
-      self->mnemonic_controller = gtk_shortcut_controller_new ();
-      gtk_event_controller_set_propagation_phase (self->mnemonic_controller, GTK_PHASE_CAPTURE);
-      gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller), 
GTK_SHORTCUT_SCOPE_MANAGED);
-      shortcut = gtk_shortcut_new (gtk_mnemonic_trigger_new (self->mnemonic_keyval),
-                                   g_object_ref (gtk_mnemonic_action_get ()));
-      gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller), shortcut);
-      gtk_widget_add_controller (GTK_WIDGET (self), self->mnemonic_controller);
     }
   else
     {
-      shortcut = g_list_model_get_item (G_LIST_MODEL (self->mnemonic_controller), 0);
-      gtk_shortcut_set_trigger (shortcut, gtk_mnemonic_trigger_new (self->mnemonic_keyval));
-      g_object_unref (shortcut);
+      *smallest = *widest;
+      *smallest_baseline = *widest_baseline;
     }
 
-  /* Connect to notify::mnemonics-visible of the root */
-  native = gtk_widget_get_native (GTK_WIDGET (self));
-  if (!GTK_IS_WINDOW (native) && !GTK_IS_POPOVER (native))
-    return;
-
-  /* always set up this widgets initial value */
-  g_object_get (native, "mnemonics-visible", &mnemonics_visible, NULL);
-  self->mnemonics_visible = mnemonics_visible;
-
-  connected =
-    GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (native), quark_mnemonics_visible_connected));
-
-  if (!connected)
-    {
-      g_signal_connect (native,
-                        "notify::mnemonics-visible",
-                        G_CALLBACK (label_mnemonics_visible_changed),
-                        self);
-      g_object_set_qdata (G_OBJECT (native),
-                          quark_mnemonics_visible_connected,
-                          GINT_TO_POINTER (1));
-    }
+  g_object_unref (layout);
 }
 
 static void
-gtk_label_root (GtkWidget *widget)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->root (widget);
+gtk_label_get_preferred_size (GtkWidget      *widget,
+                              GtkOrientation  orientation,
+                              int            *minimum_size,
+                              int            *natural_size,
+                              int            *minimum_baseline,
+                              int            *natural_baseline)
+{
+  GtkLabel      *self = GTK_LABEL (widget);
+  PangoRectangle widest_rect;
+  PangoRectangle smallest_rect;
+  int smallest_baseline;
+  int widest_baseline;
 
-  gtk_label_setup_mnemonic (self);
+  gtk_label_get_preferred_layout_size (self,
+                                       &smallest_rect, &widest_rect,
+                                       &smallest_baseline, &widest_baseline);
 
-  /* The PangoContext is replaced when the display changes, so clear the layouts */
-  gtk_label_clear_layout (GTK_LABEL (widget));
-}
+  widest_rect.width  = PANGO_PIXELS_CEIL (widest_rect.width);
+  widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
 
-static void
-gtk_label_unroot (GtkWidget *widget)
-{
-  GtkLabel *self = GTK_LABEL (widget);
+  smallest_rect.width  = PANGO_PIXELS_CEIL (smallest_rect.width);
+  smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
 
-  gtk_label_setup_mnemonic (self);
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      /* Normal desired width */
+      *minimum_size = smallest_rect.width;
+      *natural_size = widest_rect.width;
 
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->unroot (widget);
-}
+      if (minimum_baseline)
+        *minimum_baseline = -1;
 
-void
-_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
-                                                gboolean   visible)
-{
-  if (GTK_IS_LABEL (widget))
+      if (natural_baseline)
+        *natural_baseline = -1;
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
     {
-      GtkLabel *self = GTK_LABEL (widget);
-
-      if (self->mnemonics_visible != visible)
+      if (smallest_rect.height < widest_rect.height)
         {
-          self->mnemonics_visible = visible;
-          gtk_label_recalculate (self);
+          *minimum_size = smallest_rect.height;
+          *natural_size = widest_rect.height;
+          if (minimum_baseline)
+            *minimum_baseline = smallest_baseline;
+          if (natural_baseline)
+            *natural_baseline = widest_baseline;
         }
-    }
-  else
-    {
-      GtkWidget *child;
-
-      for (child = gtk_widget_get_first_child (widget);
-           child;
-           child = gtk_widget_get_next_sibling (child))
+      else
         {
-          if (GTK_IS_NATIVE (child))
-            continue;
-
-          _gtk_label_mnemonics_visible_apply_recursively (child, visible);
+          *minimum_size = widest_rect.height;
+          *natural_size = smallest_rect.height;
+          if (minimum_baseline)
+            *minimum_baseline = widest_baseline;
+          if (natural_baseline)
+            *natural_baseline = smallest_baseline;
         }
     }
 }
+
+
 static void
-label_mnemonic_widget_weak_notify (gpointer      data,
-                                  GObject      *where_the_object_was)
+gtk_label_measure (GtkWidget      *widget,
+                   GtkOrientation  orientation,
+                   int             for_size,
+                   int            *minimum,
+                   int            *natural,
+                   int            *minimum_baseline,
+                   int            *natural_baseline)
 {
-  GtkLabel *self = data;
+  GtkLabel *self = GTK_LABEL (widget);
 
-  self->mnemonic_widget = NULL;
-  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
+  if (orientation == GTK_ORIENTATION_VERTICAL && for_size != -1 && self->wrap)
+    {
+      gtk_label_clear_layout (self);
+
+      get_height_for_width (self, for_size, minimum, natural, minimum_baseline, natural_baseline);
+    }
+  else
+    gtk_label_get_preferred_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
 }
 
-/**
- * gtk_label_set_mnemonic_widget:
- * @self: a #GtkLabel
- * @widget: (nullable): the target #GtkWidget, or %NULL to unset
- *
- * If the label has been set so that it has a mnemonic key (using
- * i.e. gtk_label_set_markup_with_mnemonic(),
- * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
- * or the “use_underline” property) the label can be associated with a
- * widget that is the target of the mnemonic. When the label is inside
- * a widget (like a #GtkButton or a #GtkNotebook tab) it is
- * automatically associated with the correct widget, but sometimes
- * (i.e. when the target is a #GtkEntry next to the label) you need to
- * set it explicitly using this function.
- *
- * The target widget will be accelerated by emitting the 
- * GtkWidget::mnemonic-activate signal on it. The default handler for 
- * this signal will activate the widget if there are no mnemonic collisions 
- * and toggle focus between the colliding widgets otherwise.
- **/
-void
-gtk_label_set_mnemonic_widget (GtkLabel  *self,
-                              GtkWidget *widget)
+static void
+get_layout_location (GtkLabel  *self,
+                     int       *xp,
+                     int       *yp)
 {
-  g_return_if_fail (GTK_IS_LABEL (self));
+  GtkWidget *widget = GTK_WIDGET (self);
+  int layout_width, layout_height, x, y;
+  float xalign, yalign;
+  PangoRectangle logical;
+  int baseline, layout_baseline, baseline_offset;
+  int widget_width, widget_height;
 
-  if (widget)
-    g_return_if_fail (GTK_IS_WIDGET (widget));
+  xalign = self->xalign;
+  yalign = self->yalign;
 
-  if (self->mnemonic_widget)
-    {
-      gtk_widget_remove_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
-      g_object_weak_unref (G_OBJECT (self->mnemonic_widget),
-                           label_mnemonic_widget_weak_notify,
-                           self);
-    }
-  self->mnemonic_widget = widget;
-  if (self->mnemonic_widget)
-    {
-      g_object_weak_ref (G_OBJECT (self->mnemonic_widget),
-                         label_mnemonic_widget_weak_notify,
-                         self);
-      gtk_widget_add_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
-    }
+  if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
+    xalign = 1.0 - xalign;
 
-  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
-}
+  pango_layout_get_pixel_extents (self->layout, NULL, &logical);
 
-/**
- * gtk_label_get_mnemonic_widget:
- * @self: a #GtkLabel
- *
- * Retrieves the target of the mnemonic (keyboard shortcut) of this
- * label. See gtk_label_set_mnemonic_widget().
- *
- * Returns: (nullable) (transfer none): the target of the label’s mnemonic,
- *     or %NULL if none has been set and the default algorithm will be used.
- **/
-GtkWidget *
-gtk_label_get_mnemonic_widget (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+  layout_width  = logical.width;
+  layout_height = logical.height;
 
-  return self->mnemonic_widget;
-}
+  widget_width = gtk_widget_get_width (widget);
+  widget_height = gtk_widget_get_height (widget);
 
-/**
- * gtk_label_get_mnemonic_keyval:
- * @self: a #GtkLabel
- *
- * If the label has been set so that it has a mnemonic key this function
- * returns the keyval used for the mnemonic accelerator. If there is no
- * mnemonic set up it returns #GDK_KEY_VoidSymbol.
- *
- * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
- **/
-guint
-gtk_label_get_mnemonic_keyval (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), GDK_KEY_VoidSymbol);
+  baseline = gtk_widget_get_allocated_baseline (widget);
 
-  return self->mnemonic_keyval;
-}
+  x = floor ((xalign * (widget_width - layout_width)) - logical.x);
 
-static void
-gtk_label_set_text_internal (GtkLabel *self,
-                             char     *str)
-{
-  if (g_strcmp0 (self->text, str) == 0)
+  baseline_offset = 0;
+  if (baseline != -1)
     {
-      g_free (str);
-      return;
+      layout_baseline = pango_layout_get_baseline (self->layout) / PANGO_SCALE;
+      baseline_offset = baseline - layout_baseline;
+      yalign = 0.0; /* Can't support yalign while baseline aligning */
     }
 
-  g_free (self->text);
-  self->text = str;
+  y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
 
-  gtk_accessible_update_property (GTK_ACCESSIBLE (self),
-                                  GTK_ACCESSIBLE_PROPERTY_LABEL, self->text,
-                                  -1);
+  if (xp)
+    *xp = x;
 
-  gtk_label_select_region_index (self, 0, 0);
+  if (yp)
+    *yp = y;
 }
 
-static gboolean
-gtk_label_set_label_internal (GtkLabel   *self,
-                              const char *str)
+static void
+gtk_label_size_allocate (GtkWidget *widget,
+                         int        width,
+                         int        height,
+                         int        baseline)
 {
-  if (g_strcmp0 (str, self->label) == 0)
-    return FALSE;
-
-  g_free (self->label);
-  self->label = g_strdup (str ? str : "");
+  GtkLabel *self = GTK_LABEL (widget);
 
-  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_LABEL]);
+  if (self->layout)
+    {
+      if (self->ellipsize || self->wrap)
+        pango_layout_set_width (self->layout, width * PANGO_SCALE);
+      else
+        pango_layout_set_width (self->layout, -1);
+    }
 
-  return TRUE;
+  if (self->popup_menu)
+    gtk_popover_present (GTK_POPOVER (self->popup_menu));
 }
 
-static gboolean
-gtk_label_set_use_markup_internal (GtkLabel *self,
-                                   gboolean  val)
-{
-  if (self->use_markup != val)
-    {
-      self->use_markup = val;
-
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_MARKUP]);
 
-      return TRUE;
-    }
 
-  return FALSE;
-}
+#define GRAPHENE_RECT_FROM_RECT(_r) (GRAPHENE_RECT_INIT ((_r)->x, (_r)->y, (_r)->width, (_r)->height))
 
-static gboolean
-gtk_label_set_use_underline_internal (GtkLabel *self,
-                                      gboolean  val)
+static void
+gtk_label_snapshot (GtkWidget   *widget,
+                    GtkSnapshot *snapshot)
 {
-  if (self->use_underline != val)
-    {
-      self->use_underline = val;
+  GtkLabel *self = GTK_LABEL (widget);
+  GtkLabelSelectionInfo *info;
+  GtkStyleContext *context;
+  int lx, ly;
+  int width, height;
 
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_UNDERLINE]);
+  if (!self->text || (*self->text == '\0'))
+    return;
 
-      return TRUE;
-    }
+  gtk_label_ensure_layout (self);
 
-  return FALSE;
-}
+  context = _gtk_widget_get_style_context (widget);
+  get_layout_location (self, &lx, &ly);
 
-/* Calculates text, attrs and mnemonic_keyval from
- * label, use_underline and use_markup
- */
-static void
-gtk_label_recalculate (GtkLabel *self)
-{
-  guint keyval = self->mnemonic_keyval;
+  gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
 
-  gtk_label_clear_links (self);
-  gtk_label_clear_layout (self);
-  gtk_label_clear_select_info (self);
+  info = self->select_info;
+  if (!info)
+    return;
 
-  if (self->use_markup || self->use_underline)
-    gtk_label_set_markup_internal (self, self->label, self->use_underline);
-  else
+  width = gtk_widget_get_width (widget);
+  height = gtk_widget_get_height (widget);
+
+  if (info->selection_anchor != info->selection_end)
     {
-      g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
+      int range[2];
+      cairo_region_t *range_clip;
+      cairo_rectangle_int_t clip_rect;
+      int i;
 
-      gtk_label_set_text_internal (self, g_strdup (self->label));
-    }
+      range[0] = MIN (info->selection_anchor, info->selection_end);
+      range[1] = MAX (info->selection_anchor, info->selection_end);
 
-  if (!self->use_underline)
-    self->mnemonic_keyval = GDK_KEY_VoidSymbol;
+      gtk_style_context_save_to_node (context, info->selection_node);
 
-  if (keyval != self->mnemonic_keyval)
-    {
-      gtk_label_setup_mnemonic (self);
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_KEYVAL]);
+      range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+      for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
+        {
+          cairo_region_get_rectangle (range_clip, i, &clip_rect);
+
+          gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
+          gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
+          gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+          gtk_snapshot_pop (snapshot);
+        }
+
+      cairo_region_destroy (range_clip);
+
+      gtk_style_context_restore (context);
     }
+  else
+    {
+      GtkLabelLink *focus_link;
+      GtkLabelLink *active_link;
+      int range[2];
+      cairo_region_t *range_clip;
+      cairo_rectangle_int_t clip_rect;
+      int i;
+      GdkRectangle rect;
 
-  gtk_widget_queue_resize (GTK_WIDGET (self));
-}
+      if (info->selectable &&
+          gtk_widget_has_focus (widget) &&
+          gtk_widget_is_drawable (widget))
+        {
+          PangoDirection cursor_direction;
 
-/**
- * gtk_label_set_text:
- * @self: a #GtkLabel
- * @str: The text you want to set
- *
- * Sets the text within the #GtkLabel widget. It overwrites any text that
- * was there before.
- *
- * This function will clear any previously set mnemonic accelerators, and
- * set the #GtkLabel:use-underline property to %FALSE as a side effect.
- *
- * This function will set the #GtkLabel:use-markup property to %FALSE
- * as a side effect.
- *
- * See also: gtk_label_set_markup()
- **/
-void
-gtk_label_set_text (GtkLabel    *self,
-                    const char *str)
-{
-  gboolean changed;
+          cursor_direction = get_cursor_direction (self);
+          gtk_snapshot_render_insertion_cursor (snapshot, context,
+                                                lx, ly,
+                                                self->layout, self->select_info->selection_end,
+                                                cursor_direction);
+        }
 
-  g_return_if_fail (GTK_IS_LABEL (self));
+      focus_link = gtk_label_get_focus_link (self, NULL);
+      active_link = info->active_link;
 
-  g_object_freeze_notify (G_OBJECT (self));
+      if (active_link)
+        {
+          range[0] = active_link->start;
+          range[1] = active_link->end;
 
-  changed = gtk_label_set_label_internal (self, str);
-  changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
-  changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
+          gtk_style_context_save_to_node (context, active_link->cssnode);
 
-  if (changed)
-    gtk_label_recalculate (self);
+          range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+          for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
+            {
+              cairo_region_get_rectangle (range_clip, i, &clip_rect);
 
-  g_object_thaw_notify (G_OBJECT (self));
-}
+              gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
+              gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
+              gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+              gtk_snapshot_pop (snapshot);
+            }
 
-/**
- * gtk_label_set_attributes:
- * @self: a #GtkLabel
- * @attrs: (nullable): a #PangoAttrList, or %NULL
- *
- * Sets a #PangoAttrList; the attributes in the list are applied to the
- * label text.
- *
- * The attributes set with this function will be applied
- * and merged with any other attributes previously effected by way
- * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
- * While it is not recommended to mix markup strings with manually set
- * attributes, if you must; know that the attributes will be applied
- * to the label after the markup string is parsed.
- **/
-void
-gtk_label_set_attributes (GtkLabel         *self,
-                          PangoAttrList    *attrs)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
+          cairo_region_destroy (range_clip);
 
-  if (!attrs && !self->attrs)
-    return;
+          gtk_style_context_restore (context);
+        }
 
-  if (attrs)
-    pango_attr_list_ref (attrs);
+      if (focus_link && gtk_widget_has_visible_focus (widget))
+        {
+          range[0] = focus_link->start;
+          range[1] = focus_link->end;
 
-  if (self->attrs)
-    pango_attr_list_unref (self->attrs);
-  self->attrs = attrs;
+          gtk_style_context_save_to_node (context, focus_link->cssnode);
 
-  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ATTRIBUTES]);
+          range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
+          cairo_region_get_extents (range_clip, &rect);
 
-  gtk_label_clear_layout (self);
-  gtk_widget_queue_resize (GTK_WIDGET (self));
-}
+          gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
 
-/**
- * gtk_label_get_attributes:
- * @self: a #GtkLabel
- *
- * Gets the attribute list that was set on the label using
- * gtk_label_set_attributes(), if any. This function does
- * not reflect attributes that come from the labels markup
- * (see gtk_label_set_markup()). If you want to get the
- * effective attributes for the label, use
- * pango_layout_get_attribute (gtk_label_get_layout (self)).
- *
- * Returns: (nullable) (transfer none): the attribute list, or %NULL
- *     if none was set.
- **/
-PangoAttrList *
-gtk_label_get_attributes (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+          cairo_region_destroy (range_clip);
 
-  return self->attrs;
+          gtk_style_context_restore (context);
+        }
+    }
 }
 
-/**
- * gtk_label_set_label:
- * @self: a #GtkLabel
- * @str: the new text to set for the label
- *
- * Sets the text of the label. The label is interpreted as
- * including embedded underlines and/or Pango markup depending
- * on the values of the #GtkLabel:use-underline and
- * #GtkLabel:use-markup properties.
- **/
-void
-gtk_label_set_label (GtkLabel    *self,
-                     const char *str)
+static GtkSizeRequestMode
+gtk_label_get_request_mode (GtkWidget *widget)
 {
-  g_return_if_fail (GTK_IS_LABEL (self));
-
-  g_object_freeze_notify (G_OBJECT (self));
+  GtkLabel *self = GTK_LABEL (widget);
 
-  if (gtk_label_set_label_internal (self, str))
-    gtk_label_recalculate (self);
+  if (self->wrap)
+    return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
 
-  g_object_thaw_notify (G_OBJECT (self));
+  return GTK_SIZE_REQUEST_CONSTANT_SIZE;
 }
 
-/**
- * gtk_label_get_label:
- * @self: a #GtkLabel
- *
- * Fetches the text from a label widget including any embedded
- * underlines indicating mnemonics and Pango markup. (See
- * gtk_label_get_text()).
- *
- * Returns: the text of the label widget. This string is
- *   owned by the widget and must not be modified or freed.
- **/
-const char *
-gtk_label_get_label (GtkLabel *self)
+static void
+gtk_label_dispose (GObject *object)
 {
-  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+  GtkLabel *self = GTK_LABEL (object);
 
-  return self->label;
+  gtk_label_set_mnemonic_widget (self, NULL);
+
+  G_OBJECT_CLASS (gtk_label_parent_class)->dispose (object);
 }
 
-typedef struct
+static void
+gtk_label_clear_links (GtkLabel *self)
 {
-  GtkLabel *label;
-  GArray *links;
-  GString *new_str;
-  gsize text_len;
-} UriParserData;
+  guint i;
 
-static void
-start_element_handler (GMarkupParseContext  *context,
-                       const char           *element_name,
-                       const char          **attribute_names,
-                       const char          **attribute_values,
-                       gpointer              user_data,
-                       GError              **error)
-{
-  UriParserData *pdata = user_data;
-  GtkLabel *self = pdata->label;
+  if (!self->select_info)
+    return;
 
-  if (strcmp (element_name, "a") == 0)
+  for (i = 0; i < self->select_info->n_links; i++)
     {
-      GtkLabelLink link;
-      const char *uri = NULL;
-      const char *title = NULL;
-      const char *class = NULL;
-      gboolean visited = FALSE;
-      int line_number;
-      int char_number;
-      int i;
-      GtkCssNode *widget_node;
-      GtkStateFlags state;
-
-      g_markup_parse_context_get_position (context, &line_number, &char_number);
+      const GtkLabelLink *link = &self->select_info->links[i];
+      gtk_css_node_set_parent (link->cssnode, NULL);
+      g_free (link->uri);
+      g_free (link->title);
+    }
+  g_free (self->select_info->links);
+  self->select_info->links = NULL;
+  self->select_info->n_links = 0;
+  self->select_info->active_link = NULL;
+  gtk_widget_remove_css_class (GTK_WIDGET (self), "link");
+}
 
-      for (i = 0; attribute_names[i] != NULL; i++)
-        {
-          const char *attr = attribute_names[i];
+static void
+gtk_label_finalize (GObject *object)
+{
+  GtkLabel *self = GTK_LABEL (object);
 
-          if (strcmp (attr, "href") == 0)
-            uri = attribute_values[i];
-          else if (strcmp (attr, "title") == 0)
-            title = attribute_values[i];
-          else if (strcmp (attr, "class") == 0)
-            class = 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;
-            }
-        }
+  g_free (self->label);
+  g_free (self->text);
 
-      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;
-        }
+  g_clear_object (&self->layout);
+  g_clear_pointer (&self->attrs, pango_attr_list_unref);
+  g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
 
-      visited = FALSE;
-      if (self->select_info)
-        {
-          for (i = 0; i < self->select_info->n_links; i++)
-            {
-              const GtkLabelLink *l = &self->select_info->links[i];
+  if (self->select_info)
+    g_object_unref (self->select_info->provider);
 
-              if (strcmp (uri, l->uri) == 0)
-                {
-                  visited = l->visited;
-                  break;
-                }
-            }
-        }
+  gtk_label_clear_links (self);
+  g_free (self->select_info);
 
-      if (!pdata->links)
-        pdata->links = g_array_new (FALSE, TRUE, sizeof (GtkLabelLink));
+  g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
+  g_clear_object (&self->extra_menu);
 
-      link.uri = g_strdup (uri);
-      link.title = g_strdup (title);
+  G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
+}
 
-      widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
-      link.cssnode = gtk_css_node_new ();
-      gtk_css_node_set_name (link.cssnode, g_quark_from_static_string ("link"));
-      gtk_css_node_set_parent (link.cssnode, widget_node);
-      if (class)
-        gtk_css_node_add_class (link.cssnode, g_quark_from_string (class));
+static void
+gtk_label_unrealize (GtkWidget *widget)
+{
+  GtkLabel *self = GTK_LABEL (widget);
 
-      state = gtk_css_node_get_state (widget_node);
-      if (visited)
-        state |= GTK_STATE_FLAG_VISITED;
-      else
-        state |= GTK_STATE_FLAG_LINK;
-      gtk_css_node_set_state (link.cssnode, state);
-      g_object_unref (link.cssnode);
+  if (self->select_info &&
+      self->select_info->provider)
+    {
+      GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (widget);
 
-      link.visited = visited;
-      link.start = pdata->text_len;
-      g_array_append_val (pdata->links, link);
+      if (gdk_clipboard_get_content (clipboard) == self->select_info->provider)
+        gdk_clipboard_set_content (clipboard, NULL);
     }
-  else
-    {
-      int i;
 
-      g_string_append_c (pdata->new_str, '<');
-      g_string_append (pdata->new_str, element_name);
+  GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
+}
 
-      for (i = 0; attribute_names[i] != NULL; i++)
-        {
-          const char *attr  = attribute_names[i];
-          const char *value = attribute_values[i];
-          char *newvalue;
+static gboolean
+range_is_in_ellipsis_full (GtkLabel *self,
+                           int       range_start,
+                           int       range_end,
+                           int      *ellipsis_start,
+                           int      *ellipsis_end)
+{
+  PangoLayoutIter *iter;
+  gboolean in_ellipsis;
 
-          newvalue = g_markup_escape_text (value, -1);
+  if (!self->ellipsize)
+    return FALSE;
 
-          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, '\"');
+  gtk_label_ensure_layout (self);
 
-          g_free (newvalue);
-        }
-      g_string_append_c (pdata->new_str, '>');
-    }
-}
+  if (!pango_layout_is_ellipsized (self->layout))
+    return FALSE;
 
-static void
-end_element_handler (GMarkupParseContext  *context,
-                     const char           *element_name,
-                     gpointer              user_data,
-                     GError              **error)
-{
-  UriParserData *pdata = user_data;
+  iter = pango_layout_get_iter (self->layout);
 
-  if (!strcmp (element_name, "a"))
-    {
-      GtkLabelLink *link = &g_array_index (pdata->links, GtkLabelLink, pdata->links->len - 1);
-      link->end = pdata->text_len;
-    }
-  else
-    {
-      g_string_append (pdata->new_str, "</");
-      g_string_append (pdata->new_str, element_name);
-      g_string_append_c (pdata->new_str, '>');
-    }
-}
+  in_ellipsis = FALSE;
 
-static void
-text_handler (GMarkupParseContext  *context,
-              const char           *text,
-              gsize                 text_len,
-              gpointer              user_data,
-              GError              **error)
-{
-  UriParserData *pdata = user_data;
-  char *newtext;
+  do {
+    PangoLayoutRun *run;
 
-  newtext = g_markup_escape_text (text, text_len);
-  g_string_append (pdata->new_str, newtext);
-  pdata->text_len += text_len;
-  g_free (newtext);
-}
+    run = pango_layout_iter_get_run_readonly (iter);
+    if (run)
+      {
+        PangoItem *item;
 
-static const GMarkupParser markup_parser =
-{
-  start_element_handler,
-  end_element_handler,
-  text_handler,
-  NULL,
-  NULL
-};
+        item = ((PangoGlyphItem*)run)->item;
 
-static gboolean
-xml_isspace (char c)
-{
-  return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+        if (item->offset <= range_start && range_end <= item->offset + item->length)
+          {
+            if (item->analysis.flags & PANGO_ANALYSIS_FLAG_IS_ELLIPSIS)
+              {
+                if (ellipsis_start)
+                  *ellipsis_start = item->offset;
+                if (ellipsis_end)
+                  *ellipsis_end = item->offset + item->length;
+                in_ellipsis = TRUE;
+              }
+            break;
+          }
+        else if (item->offset + item->length >= range_end)
+          break;
+      }
+  } while (pango_layout_iter_next_run (iter));
+
+  pango_layout_iter_free (iter);
+
+  return in_ellipsis;
 }
 
-static void
-link_free (GtkLabelLink *link)
+static gboolean
+range_is_in_ellipsis (GtkLabel *self,
+                      int       range_start,
+                      int       range_end)
 {
-  gtk_css_node_set_parent (link->cssnode, NULL);
-  g_free (link->uri);
-  g_free (link->title);
+  return range_is_in_ellipsis_full (self, range_start, range_end, NULL, NULL);
 }
 
 static gboolean
-parse_uri_markup (GtkLabel      *self,
-                  const char    *str,
-                  char         **new_str,
-                  GtkLabelLink  **links,
-                  guint         *out_n_links,
-                  GError       **error)
+gtk_label_grab_focus (GtkWidget *widget)
 {
-  GMarkupParseContext *context;
-  const char *p, *end;
-  gsize length;
-  UriParserData pdata;
-
-  length = strlen (str);
-  p = str;
-  end = str + length;
+  GtkLabel *self = GTK_LABEL (widget);
+  gboolean select_on_focus;
+  GtkWidget *prev_focus;
 
-  pdata.label = self;
-  pdata.links = NULL;
-  pdata.new_str = g_string_sized_new (length);
-  pdata.text_len = 0;
+  if (self->select_info == NULL)
+    return FALSE;
 
-  while (p != end && xml_isspace (*p))
-    p++;
+  prev_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
 
-  context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
+  if (!GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget))
+    return FALSE;
 
-  if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
+  if (self->select_info->selectable)
     {
-      if (!g_markup_parse_context_parse (context, str, length, error))
-        goto failed;
+      g_object_get (gtk_widget_get_settings (widget),
+                    "gtk-label-select-on-focus",
+                    &select_on_focus,
+                    NULL);
+
+      if (select_on_focus && !self->in_click &&
+          !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
+        gtk_label_select_region (self, 0, -1);
     }
   else
     {
-      if (!g_markup_parse_context_parse (context, "<markup>", 8, error))
-        goto failed;
+      if (self->select_info->links && !self->in_click &&
+          !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
+        {
+          guint i;
 
-      if (!g_markup_parse_context_parse (context, str, length, error))
-        goto failed;
+          for (i = 0; i < self->select_info->n_links; i++)
+            {
+              const GtkLabelLink *link = &self->select_info->links[i];
 
-      if (!g_markup_parse_context_parse (context, "</markup>", 9, error))
-        goto failed;
+              if (!range_is_in_ellipsis (self, link->start, link->end))
+                {
+                  self->select_info->selection_anchor = link->start;
+                  self->select_info->selection_end = link->start;
+                  break;
+                }
+            }
+        }
     }
 
-  if (!g_markup_parse_context_end_parse (context, error))
-    goto failed;
+  return TRUE;
+}
 
-  g_markup_parse_context_free (context);
+static gboolean
+get_layout_index (GtkLabel *self,
+                  int       x,
+                  int       y,
+                  int      *index)
+{
+  int trailing = 0;
+  const char *cluster;
+  const char *cluster_end;
+  gboolean inside;
+  int lx, ly;
 
-  *new_str = g_string_free (pdata.new_str, FALSE);
+  *index = 0;
 
-  if (pdata.links)
-    {
-      *out_n_links = pdata.links->len;
-      *links = (GtkLabelLink *)g_array_free (pdata.links, FALSE);
-    }
-  else
-    {
-      *links = NULL;
-    }
+  gtk_label_ensure_layout (self);
+  get_layout_location (self, &lx, &ly);
 
-  return TRUE;
+  /* Translate x/y to layout position */
+  x -= lx;
+  y -= ly;
 
-failed:
-  g_markup_parse_context_free (context);
-  g_string_free (pdata.new_str, TRUE);
+  x *= PANGO_SCALE;
+  y *= PANGO_SCALE;
 
-  if (pdata.links)
-    g_array_free (pdata.links, TRUE);
+  inside = pango_layout_xy_to_index (self->layout,
+                                     x, y,
+                                     index, &trailing);
 
-  return FALSE;
+  cluster = self->text + *index;
+  cluster_end = cluster;
+  while (trailing)
+    {
+      cluster_end = g_utf8_next_char (cluster_end);
+      --trailing;
+    }
+
+  *index += (cluster_end - cluster);
+
+  return inside;
 }
 
-static void
-gtk_label_ensure_has_tooltip (GtkLabel *self)
+static gboolean
+gtk_label_query_tooltip (GtkWidget  *widget,
+                         int         x,
+                         int         y,
+                         gboolean    keyboard_tip,
+                         GtkTooltip *tooltip)
 {
-  guint i;
-  gboolean has_tooltip = FALSE;
+  GtkLabel *self = GTK_LABEL (widget);
+  GtkLabelSelectionInfo *info = self->select_info;
+  int index = -1;
 
-  for (i = 0; i < self->select_info->n_links; i++)
+  if (info && info->links)
     {
-      const GtkLabelLink *link = &self->select_info->links[i];
+      if (keyboard_tip)
+        {
+          if (info->selection_anchor == info->selection_end)
+            index = info->selection_anchor;
+        }
+      else
+        {
+          if (!get_layout_index (self, x, y, &index))
+            index = -1;
+        }
 
-      if (link->title)
+      if (index != -1)
         {
-          has_tooltip = TRUE;
-          break;
+          const int link_index = _gtk_label_get_link_at (self, index);
+
+          if (link_index != -1)
+            {
+              const GtkLabelLink *link = &info->links[link_index];
+
+              if (link->title)
+                {
+                  gtk_tooltip_set_markup (tooltip, link->title);
+                }
+            }
         }
     }
 
-  gtk_widget_set_has_tooltip (GTK_WIDGET (self), has_tooltip);
+  return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
+                                                                   x, y,
+                                                                   keyboard_tip,
+                                                                   tooltip);
 }
 
-/* Reads @text and extracts the accel key, if any.
- * @new_text will be set to the given text with the first _ removed.
- *
- * Returned will be the one underline attribute used for the mnemonic
- * */
-static void
-extract_mnemonic_keyval (const char      *text,
-                         guint           *out_accel_key,
-                         char           **out_new_text,
-                         PangoAttribute **out_mnemonic_attribute)
+static gboolean
+gtk_label_focus (GtkWidget        *widget,
+                 GtkDirectionType  direction)
 {
-  const gsize text_len = strlen (text);
-  gunichar c;
-  const char *p;
+  GtkLabel *self = GTK_LABEL (widget);
+  GtkLabelSelectionInfo *info = self->select_info;
+  GtkLabelLink *focus_link;
 
-  p = text;
-  for (;;)
+  if (!gtk_widget_is_focus (widget))
     {
-      const char *_index;
+      gtk_widget_grab_focus (widget);
+      if (info)
+        {
+          focus_link = gtk_label_get_focus_link (self, NULL);
+          if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
+            {
+              int i;
+              for (i = info->n_links - 1; i >= 0; i--)
+                {
+                  focus_link = &info->links[i];
+                  if (!range_is_in_ellipsis (self, focus_link->start, focus_link->end))
+                    {
+                      info->selection_anchor = focus_link->start;
+                      info->selection_end = focus_link->start;
+                    }
+                }
+            }
 
-      c = g_utf8_get_char (p);
+          return TRUE;
+        }
 
-      if (c == '\0')
-        break;
+      return FALSE;
+    }
 
-      if (c != '_')
-        {
-          p = g_utf8_next_char (p);
-          continue;
-        }
+  if (!info)
+    return FALSE;
 
-      _index = p;
+  if (info->selectable)
+    {
+      int index;
 
-      p = g_utf8_next_char (p);
-      c = g_utf8_get_char (p);
+      if (info->selection_anchor != info->selection_end)
+        goto out;
 
-      if (c != '_' && c != '\0')
-        {
-          const gsize byte_index = p - text - 1; /* Of the _ */
+      index = info->selection_anchor;
 
-          /* c is the accel key */
-          if (out_accel_key)
-            *out_accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
-          if (out_new_text)
+      if (direction == GTK_DIR_TAB_FORWARD)
+        {
+          guint i;
+          for (i = 0; i < info->n_links; i++)
             {
-              *out_new_text = g_malloc (text_len);
-              memcpy (*out_new_text, text, byte_index);
-              memcpy (*out_new_text + byte_index, p, text_len - byte_index);
-            }
+              const GtkLabelLink *link = &info->links[i];
 
-          if (out_mnemonic_attribute)
-            {
-              PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
-              attr->start_index = _index - text;
-              attr->end_index = p - text;
-              *out_mnemonic_attribute = attr;
+              if (link->start > index)
+                {
+                  if (!range_is_in_ellipsis (self, link->start, link->end))
+                    {
+                      gtk_label_select_region_index (self, link->start, link->start);
+                      return TRUE;
+                    }
+                }
             }
-
-          return;
         }
+      else if (direction == GTK_DIR_TAB_BACKWARD)
+        {
+          int i;
+          for (i = info->n_links - 1; i >= 0; i--)
+            {
+              GtkLabelLink *link = &info->links[i];
 
-      p = g_utf8_next_char (p);
-    }
+              if (link->end < index)
+                {
+                  if (!range_is_in_ellipsis (self, link->start, link->end))
+                    {
+                      gtk_label_select_region_index (self, link->start, link->start);
+                      return TRUE;
+                    }
+                }
+            }
+        }
 
-  /* No accel key found */
-  if (out_accel_key)
-    *out_accel_key = GDK_KEY_VoidSymbol;
-  if (out_new_text)
-    *out_new_text = NULL;
-  if (out_mnemonic_attribute)
-    *out_mnemonic_attribute = NULL;
+      goto out;
+    }
+  else
+    {
+      int focus_link_index;
+      int new_index = -1;
+      int i;
+
+      if (info->n_links == 0)
+        goto out;
+
+      focus_link = gtk_label_get_focus_link (self, &focus_link_index);
+
+      if (!focus_link)
+        goto out;
+
+      switch (direction)
+        {
+        case GTK_DIR_TAB_FORWARD:
+          if (focus_link)
+            new_index = (focus_link_index + 1) % info->n_links;
+          else
+            new_index = 0;
+
+          for (i = new_index; i < info->n_links; i++)
+            {
+              const GtkLabelLink *link = &info->links[i];
+              if (!range_is_in_ellipsis (self, link->start, link->end))
+                break;
+            }
+          break;
+
+        case GTK_DIR_TAB_BACKWARD:
+          if (focus_link)
+            new_index = focus_link_index == 0 ? info->n_links  - 1 : focus_link_index - 1;
+          else
+            new_index = info->n_links - 1;
+
+          for (i = new_index; i >= 0; i--)
+            {
+              const GtkLabelLink *link = &info->links[i];
+              if (!range_is_in_ellipsis (self, link->start, link->end))
+                break;
+            }
+          break;
+
+        default:
+        case GTK_DIR_UP:
+        case GTK_DIR_DOWN:
+        case GTK_DIR_LEFT:
+        case GTK_DIR_RIGHT:
+          goto out;
+        }
+
+      if (new_index != -1)
+        {
+          focus_link = &info->links[new_index];
+          info->selection_anchor = focus_link->start;
+          info->selection_end = focus_link->start;
+          gtk_widget_queue_draw (widget);
+
+          return TRUE;
+        }
+    }
+
+out:
+
+  return FALSE;
 }
 
 static void
-gtk_label_set_markup_internal (GtkLabel    *self,
-                               const char *str,
-                               gboolean     with_uline)
+emit_activate_link (GtkLabel     *self,
+                    GtkLabelLink *link)
 {
-  char *text = NULL;
-  GError *error = NULL;
-  PangoAttrList *attrs = NULL;
-  char *str_for_display = NULL;
-  GtkLabelLink *links = NULL;
-  guint n_links = 0;
-  PangoAttribute *mnemonic_attr = NULL;
+  gboolean handled;
 
-  if (!parse_uri_markup (self, str, &str_for_display, &links, &n_links, &error))
-    goto error_set;
+  g_signal_emit (self, signals[ACTIVATE_LINK], 0, link->uri, &handled);
 
-  if (links)
+  /* signal handler might have invalidated the layout */
+  if (!self->layout)
+    return;
+
+  if (handled && !link->visited &&
+      self->select_info && self->select_info->links)
     {
-      gtk_label_ensure_select_info (self);
-      self->select_info->links = g_steal_pointer (&links);
-      self->select_info->n_links = n_links;
-      gtk_label_ensure_has_tooltip (self);
-      gtk_widget_add_css_class (GTK_WIDGET (self), "link");
+      link->visited = TRUE;
+      update_link_state (self);
     }
+}
 
-  if (!with_uline)
-   {
-no_uline:
-      /* Extract the text to display */
-      if (!pango_parse_markup (str_for_display, -1, 0, &attrs, &text, NULL, &error))
-        goto error_set;
-   }
-  else /* Underline AND markup is a little more complicated... */
-   {
-      char *new_text = NULL;
-      guint accel_keyval;
-      gboolean auto_mnemonics = TRUE;
-      gboolean do_mnemonics = self->mnemonics_visible &&
-                              (!auto_mnemonics || gtk_widget_is_sensitive (GTK_WIDGET (self))) &&
-                              (!self->mnemonic_widget || gtk_widget_is_sensitive (self->mnemonic_widget));
+static void
+gtk_label_activate_link_open (GtkWidget  *widget,
+                              const char *name,
+                              GVariant   *parameter)
+{
+  GtkLabel *self = GTK_LABEL (widget);
+  GtkLabelLink *link = self->select_info->context_link;
 
-      /* Remove the mnemonic underline */
-      extract_mnemonic_keyval (str_for_display,
-                               &accel_keyval,
-                               &new_text,
-                               NULL);
-      if (!new_text) /* No underline found anyway */
-        goto no_uline;
+  if (link)
+    emit_activate_link (self, link);
+}
 
-      self->mnemonic_keyval = accel_keyval;
+static void
+gtk_label_activate_link_copy (GtkWidget  *widget,
+                              const char *name,
+                              GVariant   *parameter)
+{
+  GtkLabel *self = GTK_LABEL (widget);
+  GtkLabelLink *link = self->select_info->context_link;
 
-      /* Extract the text to display */
-      if (!pango_parse_markup (new_text, -1, '_',
-                               do_mnemonics ? &attrs : NULL, &text, NULL, &error))
-        goto error_set;
+  if (link)
+    {
+      GdkClipboard *clipboard;
 
-      if (do_mnemonics)
-        {
-          /* text is now the final text, but we need to parse str_for_display once again
-           * *with* the mnemonic underline so we can remove the markup tags and get the
-           * proper attribute indices */
-          char *text_for_accel;
+      clipboard = gtk_widget_get_clipboard (widget);
+      gdk_clipboard_set_text (clipboard, link->uri);
+    }
+  else
+    g_print ("no link ?!\n");
+}
 
-          if (!pango_parse_markup (str_for_display, -1, 0, NULL, &text_for_accel, NULL, &error))
-            {
-              g_free (new_text);
-              goto error_set;
-            }
+static void
+gtk_label_activate_clipboard_copy (GtkWidget  *widget,
+                                   const char *name,
+                                   GVariant   *parameter)
+{
+  g_signal_emit_by_name (widget, "copy-clipboard");
+}
 
-          extract_mnemonic_keyval (text_for_accel,
-                                   NULL,
-                                   NULL,
-                                   &mnemonic_attr);
-          g_free (text_for_accel);
-        }
+static void
+gtk_label_select_all (GtkLabel *self)
+{
+  gtk_label_select_region_index (self, 0, strlen (self->text));
+}
 
-      g_free (new_text);
-   }
+static void
+gtk_label_activate_selection_select_all (GtkWidget  *widget,
+                                         const char *name,
+                                         GVariant   *parameter)
+{
+  gtk_label_select_all (GTK_LABEL (widget));
+}
 
-  g_free (str_for_display);
+static void
+gtk_label_nop (GtkWidget  *widget,
+               const char *name,
+               GVariant   *parameter)
+{
+}
 
-  if (text)
-    gtk_label_set_text_internal (self, text);
+static gboolean
+gtk_label_mnemonic_activate (GtkWidget *widget,
+                             gboolean   group_cycling)
+{
+  GtkLabel *self = GTK_LABEL (widget);
+  GtkWidget *parent;
 
+  if (self->mnemonic_widget)
+    return gtk_widget_mnemonic_activate (self->mnemonic_widget, group_cycling);
 
-  g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
-  self->markup_attrs = attrs;
+  /* Try to find the widget to activate by traversing the
+   * widget's ancestry.
+   */
+  parent = gtk_widget_get_parent (widget);
 
-  if (mnemonic_attr)
-    pango_attr_list_insert_before (self->markup_attrs, mnemonic_attr);
+  if (GTK_IS_NOTEBOOK (parent))
+    return FALSE;
 
-  return;
+  while (parent)
+    {
+      if (gtk_widget_get_can_focus (parent) ||
+          (!group_cycling && gtk_widget_can_activate (parent)) ||
+          GTK_IS_NOTEBOOK (gtk_widget_get_parent (parent)))
+        return gtk_widget_mnemonic_activate (parent, group_cycling);
+      parent = gtk_widget_get_parent (parent);
+    }
 
-error_set:
-  g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
-             str, error->message);
-  g_error_free (error);
+  /* barf if there was nothing to activate */
+  g_warning ("Couldn't find a target for a mnemonic activation.");
+  gtk_widget_error_bell (widget);
 
+  return FALSE;
 }
 
-/**
- * gtk_label_set_markup:
- * @self: a #GtkLabel
- * @str: a markup string (see [Pango markup format][PangoMarkupFormat])
- *
- * Parses @str which is marked up with the
- * [Pango text markup language][PangoMarkupFormat], setting the
- * label’s text and attribute list based on the parse results.
- *
- * If the @str is external data, you may need to escape it with
- * g_markup_escape_text() or g_markup_printf_escaped():
- *
- * |[<!-- language="C" -->
- * GtkWidget *self = gtk_label_new (NULL);
- * const char *str = "...";
- * const char *format = "<span style=\"italic\">\%s</span>";
- * char *markup;
- *
- * markup = g_markup_printf_escaped (format, str);
- * gtk_label_set_markup (GTK_LABEL (self), markup);
- * g_free (markup);
- * ]|
- *
- * This function will set the #GtkLabel:use-markup property to %TRUE as
- * a side effect.
- *
- * If you set the label contents using the #GtkLabel:label property you
- * should also ensure that you set the #GtkLabel:use-markup property
- * accordingly.
- *
- * See also: gtk_label_set_text()
- **/
-void
-gtk_label_set_markup (GtkLabel    *self,
-                      const char *str)
+static void
+gtk_label_popup_menu (GtkWidget  *widget,
+                      const char *action_name,
+                      GVariant   *parameters)
 {
-  gboolean changed;
+  GtkLabel *self = GTK_LABEL (widget);
+
+  gtk_label_do_popup (self, -1, -1);
+}
+
+static void
+gtk_label_root (GtkWidget *widget)
+{
+  GtkLabel *self = GTK_LABEL (widget);
+
+  GTK_WIDGET_CLASS (gtk_label_parent_class)->root (widget);
+
+  gtk_label_setup_mnemonic (self);
+
+  /* The PangoContext is replaced when the display changes, so clear the layouts */
+  gtk_label_clear_layout (GTK_LABEL (widget));
+}
+
+static void
+gtk_label_unroot (GtkWidget *widget)
+{
+  GtkLabel *self = GTK_LABEL (widget);
+
+  gtk_label_setup_mnemonic (self);
+
+  GTK_WIDGET_CLASS (gtk_label_parent_class)->unroot (widget);
+}
+
+static gboolean
+gtk_label_activate_link (GtkLabel    *self,
+                         const char *uri)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+  GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
+
+  if (!GTK_IS_WINDOW (toplevel))
+    return FALSE;
+
+  gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
+
+  return TRUE;
+}
+
+static void
+gtk_label_activate_current_link (GtkLabel *self)
+{
+  GtkLabelLink *link;
+  GtkWidget *widget = GTK_WIDGET (self);
+
+  link = gtk_label_get_focus_link (self, NULL);
+
+  if (link)
+    emit_activate_link (self, link);
+  else
+    gtk_widget_activate_default (widget);
+}
+
+static void
+gtk_label_copy_clipboard (GtkLabel *self)
+{
+  if (self->text && self->select_info)
+    {
+      int start, end;
+      int len;
+      GdkClipboard *clipboard;
+
+      start = MIN (self->select_info->selection_anchor,
+                   self->select_info->selection_end);
+      end = MAX (self->select_info->selection_anchor,
+                 self->select_info->selection_end);
+
+      len = strlen (self->text);
+
+      if (end > len)
+        end = len;
+
+      if (start > len)
+        start = len;
+
+      clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self));
+
+      if (start != end)
+        {
+          char *str = g_strndup (self->text + start, end - start);
+          gdk_clipboard_set_text (clipboard, str);
+          g_free (str);
+        }
+      else
+        {
+          GtkLabelLink *link;
+
+          link = gtk_label_get_focus_link (self, NULL);
+          if (link)
+            gdk_clipboard_set_text (clipboard, link->uri);
+        }
+    }
+}
+
+static void
+gtk_label_class_init (GtkLabelClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+  gobject_class->set_property = gtk_label_set_property;
+  gobject_class->get_property = gtk_label_get_property;
+  gobject_class->finalize = gtk_label_finalize;
+  gobject_class->dispose = gtk_label_dispose;
+
+  widget_class->size_allocate = gtk_label_size_allocate;
+  widget_class->state_flags_changed = gtk_label_state_flags_changed;
+  widget_class->css_changed = gtk_label_css_changed;
+  widget_class->query_tooltip = gtk_label_query_tooltip;
+  widget_class->snapshot = gtk_label_snapshot;
+  widget_class->unrealize = gtk_label_unrealize;
+  widget_class->root = gtk_label_root;
+  widget_class->unroot = gtk_label_unroot;
+  widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
+  widget_class->grab_focus = gtk_label_grab_focus;
+  widget_class->focus = gtk_label_focus;
+  widget_class->get_request_mode = gtk_label_get_request_mode;
+  widget_class->measure = gtk_label_measure;
+
+  class->move_cursor = gtk_label_move_cursor;
+  class->copy_clipboard = gtk_label_copy_clipboard;
+  class->activate_link = gtk_label_activate_link;
+
+  /**
+   * GtkLabel::move-cursor:
+   * @entry: the object which received the signal
+   * @step: the granularity of the move, as a #GtkMovementStep
+   * @count: the number of @step units to move
+   * @extend_selection: %TRUE if the move should extend the selection
+   *
+   * The ::move-cursor signal is a
+   * [keybinding signal][GtkSignalAction]
+   * which gets emitted when the user initiates a cursor movement.
+   * If the cursor is not visible in @entry, this signal causes
+   * the viewport to be moved instead.
+   *
+   * Applications should not connect to it, but may emit it with
+   * g_signal_emit_by_name() if they need to control the cursor
+   * programmatically.
+   *
+   * The default bindings for this signal come in two variants,
+   * the variant with the Shift modifier extends the selection,
+   * the variant without the Shift modifier does not.
+   * There are too many key combinations to list them all here.
+   * - Arrow keys move by individual characters/lines
+   * - Ctrl-arrow key combinations move by words/paragraphs
+   * - Home/End keys move to the ends of the buffer
+   */
+  signals[MOVE_CURSOR] =
+    g_signal_new (I_("move-cursor"),
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (GtkLabelClass, move_cursor),
+                  NULL, NULL,
+                  _gtk_marshal_VOID__ENUM_INT_BOOLEAN,
+                  G_TYPE_NONE, 3,
+                  GTK_TYPE_MOVEMENT_STEP,
+                  G_TYPE_INT,
+                  G_TYPE_BOOLEAN);
+
+   /**
+   * GtkLabel::copy-clipboard:
+   * @self: the object which received the signal
+   *
+   * The ::copy-clipboard signal is a
+   * [keybinding signal][GtkSignalAction]
+   * which gets emitted to copy the selection to the clipboard.
+   *
+   * The default binding for this signal is Ctrl-c.
+   */
+  signals[COPY_CLIPBOARD] =
+    g_signal_new (I_("copy-clipboard"),
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (GtkLabelClass, copy_clipboard),
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE, 0);
+
+    /**
+     * GtkLabel::activate-current-link:
+     * @self: The label on which the signal was emitted
+     *
+     * A [keybinding signal][GtkSignalAction]
+     * which gets emitted when the user activates a link in the label.
+     *
+     * 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.
+     */
+    signals[ACTIVATE_CURRENT_LINK] =
+      g_signal_new_class_handler (I_("activate-current-link"),
+                                  G_TYPE_FROM_CLASS (gobject_class),
+                                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                  G_CALLBACK (gtk_label_activate_current_link),
+                                  NULL, NULL,
+                                  NULL,
+                                  G_TYPE_NONE, 0);
+
+    /**
+     * GtkLabel::activate-link:
+     * @self: The label on which the signal was emitted
+     * @uri: the URI that is activated
+     *
+     * The signal which gets emitted to activate a URI.
+     * Applications may connect to it to override the default behaviour,
+     * which is to call gtk_show_uri().
+     *
+     * Returns: %TRUE if the link has been activated
+     */
+    signals[ACTIVATE_LINK] =
+      g_signal_new (I_("activate-link"),
+                    G_TYPE_FROM_CLASS (gobject_class),
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GtkLabelClass, activate_link),
+                    _gtk_boolean_handled_accumulator, NULL,
+                    _gtk_marshal_BOOLEAN__STRING,
+                    G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+
+  /**
+   * GtkLabel:label:
+   *
+   * The contents of the label.
+   *
+   * If the string contains [Pango XML markup][PangoMarkupFormat], you will
+   * have to set the #GtkLabel:use-markup property to %TRUE in order for the
+   * label to display the markup attributes. See also gtk_label_set_markup()
+   * for a convenience function that sets both this property and the
+   * #GtkLabel:use-markup property at the same time.
+   *
+   * If the string contains underlines acting as mnemonics, you will have to
+   * set the #GtkLabel:use-underline property to %TRUE in order for the label
+   * to display them.
+   */
+  label_props[PROP_LABEL] =
+      g_param_spec_string ("label",
+                           P_("Label"),
+                           P_("The text of the label"),
+                           "",
+                           GTK_PARAM_READWRITE);
+
+  label_props[PROP_ATTRIBUTES] =
+      g_param_spec_boxed ("attributes",
+                          P_("Attributes"),
+                          P_("A list of style attributes to apply to the text of the label"),
+                          PANGO_TYPE_ATTR_LIST,
+                          GTK_PARAM_READWRITE);
+
+  label_props[PROP_USE_MARKUP] =
+      g_param_spec_boolean ("use-markup",
+                            P_("Use markup"),
+                            P_("The text of the label includes XML markup. See pango_parse_markup()"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  label_props[PROP_USE_UNDERLINE] =
+      g_param_spec_boolean ("use-underline",
+                            P_("Use underline"),
+                            P_("If set, an underline in the text indicates the next character should be used 
for the mnemonic accelerator key"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  label_props[PROP_JUSTIFY] =
+      g_param_spec_enum ("justify",
+                         P_("Justification"),
+                         P_("The alignment of the lines in the text of the label relative to each other. 
This does NOT affect the alignment of the label within its allocation. See GtkLabel:xalign for that"),
+                         GTK_TYPE_JUSTIFICATION,
+                         GTK_JUSTIFY_LEFT,
+                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkLabel:xalign:
+   *
+   * The xalign property determines the horizontal alignment of the label text
+   * inside the labels size allocation. Compare this to #GtkWidget:halign,
+   * which determines how the labels size allocation is positioned in the
+   * space available for the label.
+   */
+  label_props[PROP_XALIGN] =
+      g_param_spec_float ("xalign",
+                          P_("X align"),
+                          P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL 
layouts."),
+                          0.0, 1.0,
+                          0.5,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkLabel:yalign:
+   *
+   * The yalign property determines the vertical alignment of the label text
+   * inside the labels size allocation. Compare this to #GtkWidget:valign,
+   * which determines how the labels size allocation is positioned in the
+   * space available for the label.
+   */
+  label_props[PROP_YALIGN] =
+      g_param_spec_float ("yalign",
+                          P_("Y align"),
+                          P_("The vertical alignment, from 0 (top) to 1 (bottom)"),
+                          0.0, 1.0,
+                          0.5,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  label_props[PROP_WRAP] =
+      g_param_spec_boolean ("wrap",
+                            P_("Line wrap"),
+                            P_("If set, wrap lines if the text becomes too wide"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkLabel:wrap-mode:
+   *
+   * If line wrapping is on (see the #GtkLabel:wrap property) this controls
+   * how the line wrapping is done. The default is %PANGO_WRAP_WORD, which
+   * means wrap on word boundaries.
+   */
+  label_props[PROP_WRAP_MODE] =
+      g_param_spec_enum ("wrap-mode",
+                         P_("Line wrap mode"),
+                         P_("If wrap is set, controls how linewrapping is done"),
+                         PANGO_TYPE_WRAP_MODE,
+                         PANGO_WRAP_WORD,
+                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  label_props[PROP_SELECTABLE] =
+      g_param_spec_boolean ("selectable",
+                            P_("Selectable"),
+                            P_("Whether the label text can be selected with the mouse"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  label_props[PROP_MNEMONIC_KEYVAL] =
+      g_param_spec_uint ("mnemonic-keyval",
+                         P_("Mnemonic key"),
+                         P_("The mnemonic accelerator key for this label"),
+                         0, G_MAXUINT,
+                         GDK_KEY_VoidSymbol,
+                         GTK_PARAM_READABLE);
+
+  label_props[PROP_MNEMONIC_WIDGET] =
+      g_param_spec_object ("mnemonic-widget",
+                           P_("Mnemonic widget"),
+                           P_("The widget to be activated when the label’s mnemonic key is pressed"),
+                           GTK_TYPE_WIDGET,
+                           GTK_PARAM_READWRITE);
+
+  /**
+   * GtkLabel:ellipsize:
+   *
+   * The preferred place to ellipsize the string, if the label does
+   * not have enough room to display the entire string, specified as a
+   * #PangoEllipsizeMode.
+   *
+   * Note that setting this property to a value other than
+   * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests
+   * only enough space to display the ellipsis "...". In particular, this
+   * means that ellipsizing labels do not work well in notebook tabs, unless
+   * the #GtkNotebook tab-expand child property is set to %TRUE. Other ways
+   * to set a label's width are gtk_widget_set_size_request() and
+   * gtk_label_set_width_chars().
+   */
+  label_props[PROP_ELLIPSIZE] =
+      g_param_spec_enum ("ellipsize",
+                         P_("Ellipsize"),
+                         P_("The preferred place to ellipsize the string, if the label does not have enough 
room to display the entire string"),
+                         PANGO_TYPE_ELLIPSIZE_MODE,
+                         PANGO_ELLIPSIZE_NONE,
+                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
-  g_return_if_fail (GTK_IS_LABEL (self));
+  /**
+   * GtkLabel:width-chars:
+   *
+   * The desired width of the label, in characters. If this property is set to
+   * -1, the width will be calculated automatically.
+   *
+   * See the section on [text layout][label-text-layout]
+   * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
+   * determine the width of ellipsized and wrapped labels.
+   **/
+  label_props[PROP_WIDTH_CHARS] =
+      g_param_spec_int ("width-chars",
+                        P_("Width In Characters"),
+                        P_("The desired width of the label, in characters"),
+                        -1, G_MAXINT,
+                        -1,
+                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
-  g_object_freeze_notify (G_OBJECT (self));
+  /**
+   * GtkLabel:single-line-mode:
+   *
+   * Whether the label is in single line mode. In single line mode,
+   * the height of the label does not depend on the actual text, it
+   * is always set to ascent + descent of the font. This can be an
+   * advantage in situations where resizing the label because of text
+   * changes would be distracting, e.g. in a statusbar.
+   **/
+  label_props[PROP_SINGLE_LINE_MODE] =
+      g_param_spec_boolean ("single-line-mode",
+                            P_("Single Line Mode"),
+                            P_("Whether the label is in single line mode"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
-  changed = gtk_label_set_label_internal (self, str);
-  changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
-  changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
+  /**
+   * GtkLabel:max-width-chars:
+   *
+   * The desired maximum width of the label, in characters. If this property
+   * is set to -1, the width will be calculated automatically.
+   *
+   * See the section on [text layout][label-text-layout]
+   * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
+   * determine the width of ellipsized and wrapped labels.
+   **/
+  label_props[PROP_MAX_WIDTH_CHARS] =
+      g_param_spec_int ("max-width-chars",
+                        P_("Maximum Width In Characters"),
+                        P_("The desired maximum width of the label, in characters"),
+                        -1, G_MAXINT,
+                        -1,
+                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
-  if (changed)
-    gtk_label_recalculate (self);
+  /**
+   * GtkLabel:lines:
+   *
+   * The number of lines to which an ellipsized, wrapping label
+   * should be limited. This property has no effect if the
+   * label is not wrapping or ellipsized. Set this property to
+   * -1 if you don't want to limit the number of lines.
+   */
+  label_props[PROP_LINES] =
+      g_param_spec_int ("lines",
+                        P_("Number of lines"),
+                        P_("The desired number of lines, when ellipsizing a wrapping label"),
+                        -1, G_MAXINT,
+                        -1,
+                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
-  g_object_thaw_notify (G_OBJECT (self));
-}
+  /**
+   * GtkLabel:extra-menu:
+   *
+   * A menu model whose contents will be appended to
+   * the context menu.
+   */
+  label_props[PROP_EXTRA_MENU] =
+      g_param_spec_object ("extra-menu",
+                          P_("Extra menu"),
+                          P_("Menu model to append to the context menu"),
+                          G_TYPE_MENU_MODEL,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
-/**
- * gtk_label_set_markup_with_mnemonic:
- * @self: a #GtkLabel
- * @str: a markup string (see
- *     [Pango markup format][PangoMarkupFormat])
- *
- * Parses @str which is marked up with the
- * [Pango text markup language][PangoMarkupFormat],
- * setting the label’s text and attribute list based on the parse results.
- * If characters in @str are preceded by an underscore, they are underlined
- * indicating that they represent a keyboard accelerator called a mnemonic.
- *
- * The mnemonic key can be used to activate another widget, chosen
- * automatically, or explicitly using gtk_label_set_mnemonic_widget().
- */
-void
-gtk_label_set_markup_with_mnemonic (GtkLabel    *self,
-                                    const char *str)
-{
-  gboolean changed;
+  g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props);
 
-  g_return_if_fail (GTK_IS_LABEL (self));
+  /**
+   * GtkLabel|menu.popup:
+   *
+   * Opens the context menu.
+   */
+  gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_label_popup_menu);
 
-  g_object_freeze_notify (G_OBJECT (self));
+  /*
+   * Key bindings
+   */
 
-  changed = gtk_label_set_label_internal (self, str);
-  changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
-  changed = gtk_label_set_use_underline_internal (self, TRUE) || changed;
+  gtk_widget_class_add_binding_action (widget_class,
+                                       GDK_KEY_F10, GDK_SHIFT_MASK,
+                                       "menu.popup",
+                                       NULL);
+  gtk_widget_class_add_binding_action (widget_class,
+                                       GDK_KEY_Menu, 0,
+                                       "menu.popup",
+                                       NULL);
 
-  if (changed)
-    gtk_label_recalculate (self);
+  /* Moving the insertion point */
+  add_move_binding (widget_class, GDK_KEY_Right, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
 
-  g_object_thaw_notify (G_OBJECT (self));
-}
+  add_move_binding (widget_class, GDK_KEY_Left, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
 
-/**
- * gtk_label_get_text:
- * @self: a #GtkLabel
- * 
- * Fetches the text from a label widget, as displayed on the
- * screen. This does not include any embedded underlines
- * indicating mnemonics or Pango markup. (See gtk_label_get_label())
- * 
- * Returns: the text in the label widget. This is the internal
- *   string used by the label, and must not be modified.
- **/
-const char *
-gtk_label_get_text (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
+  add_move_binding (widget_class, GDK_KEY_KP_Right, 0,
+                    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
 
-  return self->text;
-}
+  add_move_binding (widget_class, GDK_KEY_KP_Left, 0,
+                      GTK_MOVEMENT_VISUAL_POSITIONS, -1);
 
-/**
- * gtk_label_set_justify:
- * @self: a #GtkLabel
- * @jtype: a #GtkJustification
- *
- * Sets the alignment of the lines in the text of the label relative to
- * each other. %GTK_JUSTIFY_LEFT is the default value when the widget is
- * first created with gtk_label_new(). If you instead want to set the
- * alignment of the label as a whole, use gtk_widget_set_halign() instead.
- * gtk_label_set_justify() has no effect on labels containing only a
- * single line.
- */
-void
-gtk_label_set_justify (GtkLabel        *self,
-                      GtkJustification jtype)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
-  g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
+  add_move_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
 
-  if ((GtkJustification) self->jtype != jtype)
-    {
-      self->jtype = jtype;
+  add_move_binding (widget_class, GDK_KEY_b, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
 
-      /* No real need to be this drastic, but easier than duplicating the code */
-      gtk_label_clear_layout (self);
-      
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_JUSTIFY]);
-      gtk_widget_queue_resize (GTK_WIDGET (self));
-    }
-}
+  add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK,
+                      GTK_MOVEMENT_WORDS, 1);
 
-/**
- * gtk_label_get_justify:
- * @self: a #GtkLabel
- *
- * Returns the justification of the label. See gtk_label_set_justify().
- *
- * Returns: #GtkJustification
- **/
-GtkJustification
-gtk_label_get_justify (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), 0);
+  add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, -1);
 
-  return self->jtype;
-}
+  add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, 1);
 
-/**
- * gtk_label_set_ellipsize:
- * @self: a #GtkLabel
- * @mode: a #PangoEllipsizeMode
- *
- * Sets the mode used to ellipsize (add an ellipsis: "...") to the text 
- * if there is not enough space to render the entire string.
- **/
-void
-gtk_label_set_ellipsize (GtkLabel          *self,
-                        PangoEllipsizeMode mode)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
-  g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
+  add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_WORDS, -1);
 
-  if ((PangoEllipsizeMode) self->ellipsize != mode)
-    {
-      self->ellipsize = mode;
+  /* select all */
+  gtk_widget_class_add_binding (widget_class,
+                                GDK_KEY_a, GDK_CONTROL_MASK,
+                                (GtkShortcutFunc) gtk_label_select_all,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class,
+                                GDK_KEY_slash, GDK_CONTROL_MASK,
+                                (GtkShortcutFunc) gtk_label_select_all,
+                                NULL);
 
-      /* No real need to be this drastic, but easier than duplicating the code */
-      gtk_label_clear_layout (self);
+  /* unselect all */
+  gtk_widget_class_add_binding_signal (widget_class,
+                                       GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                       "move-cursor",
+                                       "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
 
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ELLIPSIZE]);
-      gtk_widget_queue_resize (GTK_WIDGET (self));
-    }
-}
+  gtk_widget_class_add_binding_signal (widget_class,
+                                       GDK_KEY_backslash, GDK_CONTROL_MASK,
+                                       "move-cursor",
+                                       "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE);
+
+  add_move_binding (widget_class, GDK_KEY_f, GDK_ALT_MASK,
+                    GTK_MOVEMENT_WORDS, 1);
 
-/**
- * gtk_label_get_ellipsize:
- * @self: a #GtkLabel
- *
- * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
- *
- * Returns: #PangoEllipsizeMode
- **/
-PangoEllipsizeMode
-gtk_label_get_ellipsize (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), PANGO_ELLIPSIZE_NONE);
+  add_move_binding (widget_class, GDK_KEY_b, GDK_ALT_MASK,
+                    GTK_MOVEMENT_WORDS, -1);
 
-  return self->ellipsize;
-}
+  add_move_binding (widget_class, GDK_KEY_Home, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
 
-/**
- * gtk_label_set_width_chars:
- * @self: a #GtkLabel
- * @n_chars: the new desired width, in characters.
- * 
- * Sets the desired width in characters of @label to @n_chars.
- **/
-void
-gtk_label_set_width_chars (GtkLabel *self,
-                          int       n_chars)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
+  add_move_binding (widget_class, GDK_KEY_End, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
 
-  if (self->width_chars != n_chars)
-    {
-      self->width_chars = n_chars;
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WIDTH_CHARS]);
-      gtk_widget_queue_resize (GTK_WIDGET (self));
-    }
-}
+  add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
 
-/**
- * gtk_label_get_width_chars:
- * @self: a #GtkLabel
- * 
- * Retrieves the desired width of @label, in characters. See
- * gtk_label_set_width_chars().
- * 
- * Returns: the width of the label in characters.
- **/
-int
-gtk_label_get_width_chars (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), -1);
+  add_move_binding (widget_class, GDK_KEY_KP_End, 0,
+                    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
 
-  return self->width_chars;
-}
+  add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, -1);
 
-/**
- * gtk_label_set_max_width_chars:
- * @self: a #GtkLabel
- * @n_chars: the new desired maximum width, in characters.
- * 
- * Sets the desired maximum width in characters of @label to @n_chars.
- **/
-void
-gtk_label_set_max_width_chars (GtkLabel *self,
-                              int       n_chars)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
+  add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, 1);
 
-  if (self->max_width_chars != n_chars)
-    {
-      self->max_width_chars = n_chars;
+  add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, -1);
 
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MAX_WIDTH_CHARS]);
-      gtk_widget_queue_resize (GTK_WIDGET (self));
-    }
-}
+  add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK,
+                    GTK_MOVEMENT_BUFFER_ENDS, 1);
 
-/**
- * gtk_label_get_max_width_chars:
- * @self: a #GtkLabel
- * 
- * Retrieves the desired maximum width of @label, in characters. See
- * gtk_label_set_width_chars().
- * 
- * Returns: the maximum width of the label in characters.
- **/
-int
-gtk_label_get_max_width_chars (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), -1);
+  /* copy */
+  gtk_widget_class_add_binding_signal (widget_class,
+                                       GDK_KEY_c, GDK_CONTROL_MASK,
+                                       "copy-clipboard",
+                                       NULL);
 
-  return self->max_width_chars;
-}
+  gtk_widget_class_add_binding_signal (widget_class,
+                                       GDK_KEY_Return, 0,
+                                       "activate-current-link",
+                                       NULL);
+  gtk_widget_class_add_binding_signal (widget_class,
+                                       GDK_KEY_ISO_Enter, 0,
+                                       "activate-current-link",
+                                       NULL);
+  gtk_widget_class_add_binding_signal (widget_class,
+                                       GDK_KEY_KP_Enter, 0,
+                                       "activate-current-link",
+                                       NULL);
 
-/**
- * gtk_label_set_wrap:
- * @self: a #GtkLabel
- * @wrap: the setting
- *
- * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
- * lines if text exceeds the widget’s size. %FALSE lets the text get cut off
- * by the edge of the widget if it exceeds the widget size.
- *
- * Note that setting line wrapping to %TRUE does not make the label
- * wrap at its parent container’s width, because GTK widgets
- * conceptually can’t make their requisition depend on the parent
- * container’s size. For a label that wraps at a specific position,
- * set the label’s width using gtk_widget_set_size_request().
- **/
-void
-gtk_label_set_wrap (GtkLabel *self,
-                    gboolean  wrap)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
+  gtk_widget_class_set_css_name (widget_class, I_("label"));
+  gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
 
-  wrap = wrap != FALSE;
+  quark_mnemonics_visible_connected = g_quark_from_static_string ("gtk-label-mnemonics-visible-connected");
 
-  if (self->wrap != wrap)
-    {
-      self->wrap = wrap;
+  /**
+   * GtkLabel|clipboard.cut:
+   *
+   * Doesn't do anything, since text in labels can't be deleted.
+   */
+  gtk_widget_class_install_action (widget_class, "clipboard.cut", NULL,
+                                   gtk_label_nop);
 
-      gtk_label_clear_layout (self);
-      gtk_widget_queue_resize (GTK_WIDGET (self));
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP]);
-    }
-}
+  /**
+   * GtkLabel|clipboard.copy:
+   *
+   * Copies the text to the clipboard.
+   */
+  gtk_widget_class_install_action (widget_class, "clipboard.copy", NULL,
+                                   gtk_label_activate_clipboard_copy);
 
-/**
- * gtk_label_get_wrap:
- * @self: a #GtkLabel
- *
- * Returns whether lines in the label are automatically wrapped.
- * See gtk_label_set_wrap().
- *
- * Returns: %TRUE if the lines of the label are automatically wrapped.
- */
-gboolean
-gtk_label_get_wrap (GtkLabel *self)
-{
-  g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
+  /**
+   * GtkLabel|clipboard.paste:
+   *
+   * Doesn't do anything, since text in labels can't be edited.
+   */
+  gtk_widget_class_install_action (widget_class, "clipboard.paste", NULL,
+                                   gtk_label_nop);
 
-  return self->wrap;
-}
+  /**
+   * GtkLabel|selection.delete:
+   *
+   * Doesn't do anything, since text in labels can't be deleted.
+   */
+  gtk_widget_class_install_action (widget_class, "selection.delete", NULL,
+                                   gtk_label_nop);
 
-/**
- * gtk_label_set_wrap_mode:
- * @self: a #GtkLabel
- * @wrap_mode: the line wrapping mode
- *
- * If line wrapping is on (see gtk_label_set_wrap()) this controls how
- * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
- * wrap on word boundaries.
- **/
-void
-gtk_label_set_wrap_mode (GtkLabel *self,
-                         PangoWrapMode wrap_mode)
-{
-  g_return_if_fail (GTK_IS_LABEL (self));
+  /**
+   * GtkLabel|selection.select-all:
+   *
+   * Selects all of the text, if the label allows selection.
+   */
+  gtk_widget_class_install_action (widget_class, "selection.select-all", NULL,
+                                   gtk_label_activate_selection_select_all);
 
-  if (self->wrap_mode != wrap_mode)
-    {
-      self->wrap_mode = wrap_mode;
-      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP_MODE]);
+  /**
+   * GtkLabel|link.open:
+   *
+   * Opens the link, when activated on a link inside the label.
+   */
+  gtk_widget_class_install_action (widget_class, "link.open", NULL,
+                                   gtk_label_activate_link_open);
 
-      gtk_widget_queue_resize (GTK_WIDGET (self));
-    }
+  /**
+   * GtkLabel|link.copy:
+   *
+   * Copies the link to the clipboard, when activated on a link
+   * inside the label.
+   */
+  gtk_widget_class_install_action (widget_class, "link.copy", NULL,
+                                   gtk_label_activate_link_copy);
 }
 
 /**
- * gtk_label_get_wrap_mode:
- * @self: a #GtkLabel
+ * gtk_label_new:
+ * @str: (nullable): The text of the label
  *
- * Returns line wrap mode used by the label. See gtk_label_set_wrap_mode().
+ * Creates a new label with the given text inside it. You can
+ * pass %NULL to get an empty label widget.
  *
- * Returns: %TRUE if the lines of the label are automatically wrapped.
- */
-PangoWrapMode
-gtk_label_get_wrap_mode (GtkLabel *self)
+ * Returns: the new #GtkLabel
+ **/
+GtkWidget*
+gtk_label_new (const char *str)
 {
-  g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
-
-  return self->wrap_mode;
-}
+  GtkLabel *self;
 
-static void
-gtk_label_dispose (GObject *object)
-{
-  GtkLabel *self = GTK_LABEL (object);
+  self = g_object_new (GTK_TYPE_LABEL, NULL);
 
-  gtk_label_set_mnemonic_widget (self, NULL);
+  if (str && *str)
+    gtk_label_set_text (self, str);
 
-  G_OBJECT_CLASS (gtk_label_parent_class)->dispose (object);
+  return GTK_WIDGET (self);
 }
 
-static void
-gtk_label_finalize (GObject *object)
+/**
+ * gtk_label_new_with_mnemonic:
+ * @str: (nullable): The text of the label, with an underscore in front of the
+ *       mnemonic character
+ *
+ * Creates a new #GtkLabel, containing the text in @str.
+ *
+ * If characters in @str are preceded by an underscore, they are
+ * underlined. If you need a literal underscore character in a label, use
+ * '__' (two underscores). The first underlined character represents a
+ * keyboard accelerator called a mnemonic. The mnemonic key can be used
+ * to activate another widget, chosen automatically, or explicitly using
+ * gtk_label_set_mnemonic_widget().
+ *
+ * If gtk_label_set_mnemonic_widget() is not called, then the first
+ * activatable ancestor of the #GtkLabel will be chosen as the mnemonic
+ * widget. For instance, if the label is inside a button or menu item,
+ * the button or menu item will automatically become the mnemonic widget
+ * and be activated by the mnemonic.
+ *
+ * Returns: the new #GtkLabel
+ **/
+GtkWidget*
+gtk_label_new_with_mnemonic (const char *str)
 {
-  GtkLabel *self = GTK_LABEL (object);
-
-  g_free (self->label);
-  g_free (self->text);
-
-  g_clear_object (&self->layout);
-  g_clear_pointer (&self->attrs, pango_attr_list_unref);
-  g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
-
-  if (self->select_info)
-    g_object_unref (self->select_info->provider);
+  GtkLabel *self;
 
-  gtk_label_clear_links (self);
-  g_free (self->select_info);
+  self = g_object_new (GTK_TYPE_LABEL, NULL);
 
-  g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
-  g_clear_object (&self->extra_menu);
+  if (str && *str)
+    gtk_label_set_text_with_mnemonic (self, str);
 
-  G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
+  return GTK_WIDGET (self);
 }
 
 static void
-gtk_label_clear_layout (GtkLabel *self)
+label_mnemonics_visible_changed (GtkWidget  *widget,
+                                 GParamSpec *pspec,
+                                 gpointer    data)
 {
-  g_clear_object (&self->layout);
+  gboolean visible;
+
+  g_object_get (widget, "mnemonics-visible", &visible, NULL);
+  _gtk_label_mnemonics_visible_apply_recursively (widget, visible);
 }
 
-/**
- * gtk_label_get_measuring_layout:
- * @self: the label
- * @existing_layout: %NULL or an existing layout already in use.
- * @width: the width to measure with in pango units, or -1 for infinite
- *
- * Gets a layout that can be used for measuring sizes. The returned
- * layout will be identical to the label’s layout except for the
- * layout’s width, which will be set to @width. Do not modify the returned
- * layout.
- *
- * Returns: a new reference to a pango layout
- **/
-static PangoLayout *
-gtk_label_get_measuring_layout (GtkLabel    *self,
-                                PangoLayout *existing_layout,
-                                int          width)
+static void
+gtk_label_setup_mnemonic (GtkLabel *self)
 {
-  PangoLayout *copy;
+  GtkWidget *widget = GTK_WIDGET (self);
+  GtkShortcut *shortcut;
+  GtkNative *native;
+  gboolean connected;
+  gboolean mnemonics_visible;
 
-  if (existing_layout != NULL)
+  if (self->mnemonic_keyval == GDK_KEY_VoidSymbol)
     {
-      if (existing_layout != self->layout)
+      if (self->mnemonic_controller)
         {
-          pango_layout_set_width (existing_layout, width);
-          return existing_layout;
+          gtk_widget_remove_controller (widget, self->mnemonic_controller);
+          self->mnemonic_controller = NULL;
         }
+      return;
+    }
 
-      g_object_unref (existing_layout);
+  if (self->mnemonic_controller == NULL)
+    {
+      self->mnemonic_controller = gtk_shortcut_controller_new ();
+      gtk_event_controller_set_propagation_phase (self->mnemonic_controller, GTK_PHASE_CAPTURE);
+      gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller), 
GTK_SHORTCUT_SCOPE_MANAGED);
+      shortcut = gtk_shortcut_new (gtk_mnemonic_trigger_new (self->mnemonic_keyval),
+                                   g_object_ref (gtk_mnemonic_action_get ()));
+      gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (self->mnemonic_controller), shortcut);
+      gtk_widget_add_controller (GTK_WIDGET (self), self->mnemonic_controller);
+    }
+  else
+    {
+      shortcut = g_list_model_get_item (G_LIST_MODEL (self->mnemonic_controller), 0);
+      gtk_shortcut_set_trigger (shortcut, gtk_mnemonic_trigger_new (self->mnemonic_keyval));
+      g_object_unref (shortcut);
     }
 
-  gtk_label_ensure_layout (self);
+  /* Connect to notify::mnemonics-visible of the root */
+  native = gtk_widget_get_native (GTK_WIDGET (self));
+  if (!GTK_IS_WINDOW (native) && !GTK_IS_POPOVER (native))
+    return;
 
-  if (pango_layout_get_width (self->layout) == width)
+  /* always set up this widgets initial value */
+  g_object_get (native, "mnemonics-visible", &mnemonics_visible, NULL);
+  self->mnemonics_visible = mnemonics_visible;
+
+  connected = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (native),
+                                                   quark_mnemonics_visible_connected));
+
+  if (!connected)
     {
-      g_object_ref (self->layout);
-      return self->layout;
+      g_signal_connect (native,
+                        "notify::mnemonics-visible",
+                        G_CALLBACK (label_mnemonics_visible_changed),
+                        self);
+      g_object_set_qdata (G_OBJECT (native),
+                          quark_mnemonics_visible_connected,
+                          GINT_TO_POINTER (1));
     }
+}
 
-  /* 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_width (GTK_WIDGET (self)) <= 1)
+void
+_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
+                                                gboolean   visible)
+{
+  if (GTK_IS_LABEL (widget))
     {
-      g_object_ref (self->layout);
-      pango_layout_set_width (self->layout, width);
-      return self->layout;
-    }
+      GtkLabel *self = GTK_LABEL (widget);
 
-  /* oftentimes we want to measure a width that is far wider than the current width,
-   * even though the layout would not change if we made it wider. In that case, we
-   * can just return the current layout, because for measuring purposes, it will be
-   * identical.
-   */
-  if (!pango_layout_is_wrapped (self->layout) &&
-      !pango_layout_is_ellipsized (self->layout))
+      if (self->mnemonics_visible != visible)
+        {
+          self->mnemonics_visible = visible;
+          gtk_label_recalculate (self);
+        }
+    }
+  else
     {
-      PangoRectangle rect;
+      GtkWidget *child;
 
-      if (width == -1)
-        return g_object_ref (self->layout);
+      for (child = gtk_widget_get_first_child (widget);
+           child;
+           child = gtk_widget_get_next_sibling (child))
+        {
+          if (GTK_IS_NATIVE (child))
+            continue;
 
-      pango_layout_get_extents (self->layout, NULL, &rect);
-      if (rect.width <= width)
-        return g_object_ref (self->layout);
+          _gtk_label_mnemonics_visible_apply_recursively (child, visible);
+        }
     }
+}
+static void
+label_mnemonic_widget_weak_notify (gpointer      data,
+                                   GObject      *where_the_object_was)
+{
+  GtkLabel *self = data;
 
-  copy = pango_layout_copy (self->layout);
-  pango_layout_set_width (copy, width);
-  return copy;
+  self->mnemonic_widget = NULL;
+  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
 }
 
-static void
-gtk_label_update_layout_attributes (GtkLabel      *self,
-                                    PangoAttrList *style_attrs)
+/**
+ * gtk_label_set_mnemonic_widget:
+ * @self: a #GtkLabel
+ * @widget: (nullable): the target #GtkWidget, or %NULL to unset
+ *
+ * If the label has been set so that it has a mnemonic key (using
+ * i.e. gtk_label_set_markup_with_mnemonic(),
+ * gtk_label_set_text_with_mnemonic(), gtk_label_new_with_mnemonic()
+ * or the “use_underline” property) the label can be associated with a
+ * widget that is the target of the mnemonic. When the label is inside
+ * a widget (like a #GtkButton or a #GtkNotebook tab) it is
+ * automatically associated with the correct widget, but sometimes
+ * (i.e. when the target is a #GtkEntry next to the label) you need to
+ * set it explicitly using this function.
+ *
+ * The target widget will be accelerated by emitting the
+ * GtkWidget::mnemonic-activate signal on it. The default handler for
+ * this signal will activate the widget if there are no mnemonic collisions
+ * and toggle focus between the colliding widgets otherwise.
+ **/
+void
+gtk_label_set_mnemonic_widget (GtkLabel  *self,
+                               GtkWidget *widget)
 {
-  GtkWidget *widget = GTK_WIDGET (self);
-  GtkCssStyle *style;
-  PangoAttrList *attrs;
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-  if (self->layout == NULL)
+  if (widget)
+    g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  if (self->mnemonic_widget)
     {
-      pango_attr_list_unref (style_attrs);
-      return;
+      gtk_widget_remove_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
+      g_object_weak_unref (G_OBJECT (self->mnemonic_widget),
+                           label_mnemonic_widget_weak_notify,
+                           self);
     }
-
-  if (self->select_info && self->select_info->links)
+  self->mnemonic_widget = widget;
+  if (self->mnemonic_widget)
     {
-      guint i;
-
-      attrs = pango_attr_list_new ();
-
-      for (i = 0; i < self->select_info->n_links; i++)
-        {
-          const GtkLabelLink *link = &self->select_info->links[i];
-          const GdkRGBA *link_color;
-          PangoAttrList *link_attrs;
-          PangoAttribute *attr;
-
-          style = gtk_css_node_get_style (link->cssnode);
-          link_attrs = gtk_css_style_get_pango_attributes (style);
-          if (link_attrs)
-            {
-              GSList *attributes = pango_attr_list_get_attributes (link_attrs);
-              GSList *l;
-              for (l = attributes; l; l = l->next)
-                {
-                  attr = l->data;
+      g_object_weak_ref (G_OBJECT (self->mnemonic_widget),
+                         label_mnemonic_widget_weak_notify,
+                         self);
+      gtk_widget_add_mnemonic_label (self->mnemonic_widget, GTK_WIDGET (self));
+    }
 
-                  attr->start_index = link->start;
-                  attr->end_index = link->end;
-                  pango_attr_list_insert (attrs, attr);
-                }
-              g_slist_free (attributes);
-            }
+  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_WIDGET]);
+}
 
-          link_color = gtk_css_color_value_get_rgba (style->core->color);
-          attr = pango_attr_foreground_new (link_color->red * 65535,
-                                            link_color->green * 65535,
-                                            link_color->blue * 65535);
-          attr->start_index = link->start;
-          attr->end_index = link->end;
-          pango_attr_list_insert (attrs, attr);
+/**
+ * gtk_label_get_mnemonic_widget:
+ * @self: a #GtkLabel
+ *
+ * Retrieves the target of the mnemonic (keyboard shortcut) of this
+ * label. See gtk_label_set_mnemonic_widget().
+ *
+ * Returns: (nullable) (transfer none): the target of the label’s mnemonic,
+ *     or %NULL if none has been set and the default algorithm will be used.
+ **/
+GtkWidget *
+gtk_label_get_mnemonic_widget (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
 
-          pango_attr_list_unref (link_attrs);
-        }
-    }
-  else
-    attrs = NULL;
+  return self->mnemonic_widget;
+}
 
-  style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
-  if (!style_attrs)
-    style_attrs = gtk_css_style_get_pango_attributes (style);
+/**
+ * gtk_label_get_mnemonic_keyval:
+ * @self: a #GtkLabel
+ *
+ * If the label has been set so that it has a mnemonic key this function
+ * returns the keyval used for the mnemonic accelerator. If there is no
+ * mnemonic set up it returns #GDK_KEY_VoidSymbol.
+ *
+ * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
+ **/
+guint
+gtk_label_get_mnemonic_keyval (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), GDK_KEY_VoidSymbol);
 
-  if (style_attrs)
+  return self->mnemonic_keyval;
+}
+
+static void
+gtk_label_set_text_internal (GtkLabel *self,
+                             char     *str)
+{
+  if (g_strcmp0 (self->text, str) == 0)
     {
-      attrs = _gtk_pango_attr_list_merge (attrs, style_attrs);
-      pango_attr_list_unref (style_attrs);
+      g_free (str);
+      return;
     }
 
-  attrs = _gtk_pango_attr_list_merge (attrs, self->markup_attrs);
-  attrs = _gtk_pango_attr_list_merge (attrs, self->attrs);
+  g_free (self->text);
+  self->text = str;
 
-  pango_layout_set_attributes (self->layout, attrs);
+  gtk_accessible_update_property (GTK_ACCESSIBLE (self),
+                                  GTK_ACCESSIBLE_PROPERTY_LABEL, self->text,
+                                  -1);
 
-  pango_attr_list_unref (attrs);
+  gtk_label_select_region_index (self, 0, 0);
 }
 
-static void
-gtk_label_ensure_layout (GtkLabel *self)
+static gboolean
+gtk_label_set_label_internal (GtkLabel   *self,
+                              const char *str)
 {
-  PangoAlignment align;
-  gboolean rtl;
+  if (g_strcmp0 (str, self->label) == 0)
+    return FALSE;
 
-  if (self->layout)
-    return;
+  g_free (self->label);
+  self->label = g_strdup (str ? str : "");
 
-  align = PANGO_ALIGN_LEFT; /* Quiet gcc */
-  rtl = _gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
-  self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), self->text);
+  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_LABEL]);
 
-  gtk_label_update_layout_attributes (self, NULL);
+  return TRUE;
+}
 
-  switch (self->jtype)
+static gboolean
+gtk_label_set_use_markup_internal (GtkLabel *self,
+                                   gboolean  val)
+{
+  if (self->use_markup != val)
     {
-    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;
-    case GTK_JUSTIFY_FILL:
-      align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
-      pango_layout_set_justify (self->layout, TRUE);
-      break;
-    default:
-      g_assert_not_reached();
-    }
+      self->use_markup = val;
 
-  pango_layout_set_alignment (self->layout, align);
-  pango_layout_set_ellipsize (self->layout, self->ellipsize);
-  pango_layout_set_wrap (self->layout, self->wrap_mode);
-  pango_layout_set_single_paragraph_mode (self->layout, self->single_line_mode);
-  if (self->lines > 0)
-    pango_layout_set_height (self->layout, - self->lines);
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_MARKUP]);
 
-  if (self->ellipsize || self->wrap)
-    pango_layout_set_width (self->layout, gtk_widget_get_width (GTK_WIDGET (self)) * PANGO_SCALE);
+      return TRUE;
+    }
+
+  return FALSE;
 }
 
-static GtkSizeRequestMode
-gtk_label_get_request_mode (GtkWidget *widget)
+static gboolean
+gtk_label_set_use_underline_internal (GtkLabel *self,
+                                      gboolean  val)
 {
-  GtkLabel *self = GTK_LABEL (widget);
+  if (self->use_underline != val)
+    {
+      self->use_underline = val;
 
-  if (self->wrap)
-    return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_USE_UNDERLINE]);
 
-  return GTK_SIZE_REQUEST_CONSTANT_SIZE;
-}
+      return TRUE;
+    }
 
+  return FALSE;
+}
 
+/* Calculates text, attrs and mnemonic_keyval from
+ * label, use_underline and use_markup
+ */
 static void
-get_height_for_width (GtkLabel *self,
-                      int       width,
-                      int      *minimum_height,
-                      int      *natural_height,
-                      int      *minimum_baseline,
-                      int      *natural_baseline)
+gtk_label_recalculate (GtkLabel *self)
 {
-  PangoLayout *layout;
-  int text_height, baseline;
+  guint keyval = self->mnemonic_keyval;
 
-  layout = gtk_label_get_measuring_layout (self, NULL, width * PANGO_SCALE);
+  gtk_label_clear_links (self);
+  gtk_label_clear_layout (self);
+  gtk_label_clear_select_info (self);
 
-  pango_layout_get_pixel_size (layout, NULL, &text_height);
+  if (self->use_markup || self->use_underline)
+    gtk_label_set_markup_internal (self, self->label, self->use_underline);
+  else
+    {
+      g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
 
-  *minimum_height = text_height;
-  *natural_height = text_height;
+      gtk_label_set_text_internal (self, g_strdup (self->label));
+    }
 
-  baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
-  *minimum_baseline = baseline;
-  *natural_baseline = baseline;
+  if (!self->use_underline)
+    self->mnemonic_keyval = GDK_KEY_VoidSymbol;
 
-  g_object_unref (layout);
+  if (keyval != self->mnemonic_keyval)
+    {
+      gtk_label_setup_mnemonic (self);
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MNEMONIC_KEYVAL]);
+    }
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
 }
 
-static int
-get_char_pixels (GtkWidget   *self,
-                 PangoLayout *layout)
+/**
+ * gtk_label_set_text:
+ * @self: a #GtkLabel
+ * @str: The text you want to set
+ *
+ * Sets the text within the #GtkLabel widget. It overwrites any text that
+ * was there before.
+ *
+ * This function will clear any previously set mnemonic accelerators, and
+ * set the #GtkLabel:use-underline property to %FALSE as a side effect.
+ *
+ * This function will set the #GtkLabel:use-markup property to %FALSE
+ * as a side effect.
+ *
+ * See also: gtk_label_set_markup()
+ **/
+void
+gtk_label_set_text (GtkLabel    *self,
+                    const char *str)
 {
-  PangoContext *context;
-  PangoFontMetrics *metrics;
-  int 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);
+  gboolean changed;
 
-  return MAX (char_width, digit_width);;
-}
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-static void
-gtk_label_get_preferred_layout_size (GtkLabel *self,
-                                     PangoRectangle *smallest,
-                                     PangoRectangle *widest,
-                                     int *smallest_baseline,
-                                     int *widest_baseline)
-{
-  PangoLayout *layout;
-  int char_pixels;
+  g_object_freeze_notify (G_OBJECT (self));
 
-  /* "width-chars" Hard-coded minimum width:
-   *    - minimum size should be MAX (width-chars, strlen ("..."));
-   *    - natural size should be MAX (width-chars, strlen (self->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 (self->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.
-   */
+  changed = gtk_label_set_label_internal (self, str);
+  changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
+  changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
 
-  /* Start off with the pixel extents of an as-wide-as-possible layout */
-  layout = gtk_label_get_measuring_layout (self, NULL, -1);
+  if (changed)
+    gtk_label_recalculate (self);
 
-  if (self->width_chars > -1 || self->max_width_chars > -1)
-    char_pixels = get_char_pixels (GTK_WIDGET (self), layout);
-  else
-    char_pixels = 0;
+  g_object_thaw_notify (G_OBJECT (self));
+}
 
-  pango_layout_get_extents (layout, NULL, widest);
-  widest->width = MAX (widest->width, char_pixels * self->width_chars);
-  widest->x = widest->y = 0;
-  *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+/**
+ * gtk_label_set_attributes:
+ * @self: a #GtkLabel
+ * @attrs: (nullable): a #PangoAttrList, or %NULL
+ *
+ * Sets a #PangoAttrList; the attributes in the list are applied to the
+ * label text.
+ *
+ * The attributes set with this function will be applied
+ * and merged with any other attributes previously effected by way
+ * of the #GtkLabel:use-underline or #GtkLabel:use-markup properties.
+ * While it is not recommended to mix markup strings with manually set
+ * attributes, if you must; know that the attributes will be applied
+ * to the label after the markup string is parsed.
+ **/
+void
+gtk_label_set_attributes (GtkLabel         *self,
+                          PangoAttrList    *attrs)
+{
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-  if (self->ellipsize || self->wrap)
-    {
-      /* a layout with width 0 will be as small as humanly possible */
-      layout = gtk_label_get_measuring_layout (self,
-                                               layout,
-                                               self->width_chars > -1 ? char_pixels * self->width_chars
-                                                                      : 0);
+  if (!attrs && !self->attrs)
+    return;
 
-      pango_layout_get_extents (layout, NULL, smallest);
-      smallest->width = MAX (smallest->width, char_pixels * self->width_chars);
-      smallest->x = smallest->y = 0;
+  if (attrs)
+    pango_attr_list_ref (attrs);
 
-      *smallest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
+  if (self->attrs)
+    pango_attr_list_unref (self->attrs);
+  self->attrs = attrs;
 
-      if (self->max_width_chars > -1 && widest->width > char_pixels * self->max_width_chars)
-        {
-          layout = gtk_label_get_measuring_layout (self,
-                                                   layout,
-                                                   MAX (smallest->width, char_pixels * 
self->max_width_chars));
-          pango_layout_get_extents (layout, NULL, widest);
-          widest->width = MAX (widest->width, char_pixels * self->width_chars);
-          widest->x = widest->y = 0;
+  g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ATTRIBUTES]);
 
-          *widest_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE;
-        }
+  gtk_label_clear_layout (self);
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+}
 
-      if (widest->width < smallest->width)
-        {
-          *smallest = *widest;
-          *smallest_baseline = *widest_baseline;
-        }
-    }
-  else
-    {
-      *smallest = *widest;
-      *smallest_baseline = *widest_baseline;
-    }
+/**
+ * gtk_label_get_attributes:
+ * @self: a #GtkLabel
+ *
+ * Gets the attribute list that was set on the label using
+ * gtk_label_set_attributes(), if any. This function does
+ * not reflect attributes that come from the labels markup
+ * (see gtk_label_set_markup()). If you want to get the
+ * effective attributes for the label, use
+ * pango_layout_get_attribute (gtk_label_get_layout (self)).
+ *
+ * Returns: (nullable) (transfer none): the attribute list, or %NULL
+ *     if none was set.
+ **/
+PangoAttrList *
+gtk_label_get_attributes (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
 
-  g_object_unref (layout);
+  return self->attrs;
 }
 
-static void
-gtk_label_get_preferred_size (GtkWidget      *widget,
-                              GtkOrientation  orientation,
-                              int            *minimum_size,
-                              int            *natural_size,
-                             int            *minimum_baseline,
-                             int            *natural_baseline)
+/**
+ * gtk_label_set_label:
+ * @self: a #GtkLabel
+ * @str: the new text to set for the label
+ *
+ * Sets the text of the label. The label is interpreted as
+ * including embedded underlines and/or Pango markup depending
+ * on the values of the #GtkLabel:use-underline and
+ * #GtkLabel:use-markup properties.
+ **/
+void
+gtk_label_set_label (GtkLabel    *self,
+                     const char *str)
 {
-  GtkLabel      *self = GTK_LABEL (widget);
-  PangoRectangle widest_rect;
-  PangoRectangle smallest_rect;
-  int smallest_baseline;
-  int widest_baseline;
-
-  gtk_label_get_preferred_layout_size (self,
-                                       &smallest_rect, &widest_rect,
-                                       &smallest_baseline, &widest_baseline);
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-  widest_rect.width  = PANGO_PIXELS_CEIL (widest_rect.width);
-  widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
+  g_object_freeze_notify (G_OBJECT (self));
 
-  smallest_rect.width  = PANGO_PIXELS_CEIL (smallest_rect.width);
-  smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
+  if (gtk_label_set_label_internal (self, str))
+    gtk_label_recalculate (self);
 
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      /* Normal desired width */
-      *minimum_size = smallest_rect.width;
-      *natural_size = widest_rect.width;
+  g_object_thaw_notify (G_OBJECT (self));
+}
 
-      if (minimum_baseline)
-        *minimum_baseline = -1;
+/**
+ * gtk_label_get_label:
+ * @self: a #GtkLabel
+ *
+ * Fetches the text from a label widget including any embedded
+ * underlines indicating mnemonics and Pango markup. (See
+ * gtk_label_get_text()).
+ *
+ * Returns: the text of the label widget. This string is
+ *   owned by the widget and must not be modified or freed.
+ **/
+const char *
+gtk_label_get_label (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
 
-      if (natural_baseline)
-        *natural_baseline = -1;
-    }
-  else /* GTK_ORIENTATION_VERTICAL */
-    {
-      if (smallest_rect.height < widest_rect.height)
-        {
-          *minimum_size = smallest_rect.height;
-          *natural_size = widest_rect.height;
-          if (minimum_baseline)
-            *minimum_baseline = smallest_baseline;
-          if (natural_baseline)
-            *natural_baseline = widest_baseline;
-        }
-      else
-        {
-          *minimum_size = widest_rect.height;
-          *natural_size = smallest_rect.height;
-          if (minimum_baseline)
-            *minimum_baseline = widest_baseline;
-          if (natural_baseline)
-            *natural_baseline = smallest_baseline;
-        }
-    }
+  return self->label;
 }
 
+typedef struct
+{
+  GtkLabel *label;
+  GArray *links;
+  GString *new_str;
+  gsize text_len;
+} UriParserData;
+
 static void
-gtk_label_measure (GtkWidget      *widget,
-                   GtkOrientation  orientation,
-                   int             for_size,
-                   int            *minimum,
-                   int            *natural,
-                   int            *minimum_baseline,
-                   int            *natural_baseline)
+start_element_handler (GMarkupParseContext  *context,
+                       const char           *element_name,
+                       const char          **attribute_names,
+                       const char          **attribute_values,
+                       gpointer              user_data,
+                       GError              **error)
 {
-  GtkLabel *self = GTK_LABEL (widget);
+  UriParserData *pdata = user_data;
+  GtkLabel *self = pdata->label;
 
-  if (orientation == GTK_ORIENTATION_VERTICAL && for_size != -1 && self->wrap)
+  if (strcmp (element_name, "a") == 0)
     {
-      gtk_label_clear_layout (self);
+      GtkLabelLink link;
+      const char *uri = NULL;
+      const char *title = NULL;
+      const char *class = NULL;
+      gboolean visited = FALSE;
+      int line_number;
+      int char_number;
+      int i;
+      GtkCssNode *widget_node;
+      GtkStateFlags state;
 
-      get_height_for_width (self, for_size, minimum, natural, minimum_baseline, natural_baseline);
-    }
-  else
-    gtk_label_get_preferred_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
-}
+      g_markup_parse_context_get_position (context, &line_number, &char_number);
 
-static void
-get_layout_location (GtkLabel  *self,
-                     int       *xp,
-                     int       *yp)
-{
-  GtkWidget *widget = GTK_WIDGET (self);
-  int layout_width, layout_height, x, y;
-  float xalign, yalign;
-  PangoRectangle logical;
-  int baseline, layout_baseline, baseline_offset;
-  int widget_width, widget_height;
+      for (i = 0; attribute_names[i] != NULL; i++)
+        {
+          const char *attr = attribute_names[i];
 
-  xalign = self->xalign;
-  yalign = self->yalign;
+          if (strcmp (attr, "href") == 0)
+            uri = attribute_values[i];
+          else if (strcmp (attr, "title") == 0)
+            title = attribute_values[i];
+          else if (strcmp (attr, "class") == 0)
+            class = 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 (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
-    xalign = 1.0 - xalign;
+      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;
+        }
 
-  pango_layout_get_pixel_extents (self->layout, NULL, &logical);
+      visited = FALSE;
+      if (self->select_info)
+        {
+          for (i = 0; i < self->select_info->n_links; i++)
+            {
+              const GtkLabelLink *l = &self->select_info->links[i];
+
+              if (strcmp (uri, l->uri) == 0)
+                {
+                  visited = l->visited;
+                  break;
+                }
+            }
+        }
 
-  layout_width  = logical.width;
-  layout_height = logical.height;
+      if (!pdata->links)
+        pdata->links = g_array_new (FALSE, TRUE, sizeof (GtkLabelLink));
 
-  widget_width = gtk_widget_get_width (widget);
-  widget_height = gtk_widget_get_height (widget);
+      link.uri = g_strdup (uri);
+      link.title = g_strdup (title);
 
-  baseline = gtk_widget_get_allocated_baseline (widget);
+      widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
+      link.cssnode = gtk_css_node_new ();
+      gtk_css_node_set_name (link.cssnode, g_quark_from_static_string ("link"));
+      gtk_css_node_set_parent (link.cssnode, widget_node);
+      if (class)
+        gtk_css_node_add_class (link.cssnode, g_quark_from_string (class));
 
-  x = floor ((xalign * (widget_width - layout_width)) - logical.x);
+      state = gtk_css_node_get_state (widget_node);
+      if (visited)
+        state |= GTK_STATE_FLAG_VISITED;
+      else
+        state |= GTK_STATE_FLAG_LINK;
+      gtk_css_node_set_state (link.cssnode, state);
+      g_object_unref (link.cssnode);
 
-  baseline_offset = 0;
-  if (baseline != -1)
-    {
-      layout_baseline = pango_layout_get_baseline (self->layout) / PANGO_SCALE;
-      baseline_offset = baseline - layout_baseline;
-      yalign = 0.0; /* Can't support yalign while baseline aligning */
+      link.visited = visited;
+      link.start = pdata->text_len;
+      g_array_append_val (pdata->links, link);
     }
+  else
+    {
+      int i;
 
-  y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
+      g_string_append_c (pdata->new_str, '<');
+      g_string_append (pdata->new_str, element_name);
 
-  if (xp)
-    *xp = x;
+      for (i = 0; attribute_names[i] != NULL; i++)
+        {
+          const char *attr  = attribute_names[i];
+          const char *value = attribute_values[i];
+          char *newvalue;
 
-  if (yp)
-    *yp = y;
-}
+          newvalue = g_markup_escape_text (value, -1);
 
-static void
-gtk_label_size_allocate (GtkWidget *widget,
-                         int        width,
-                         int        height,
-                         int        baseline)
-{
-  GtkLabel *self = GTK_LABEL (widget);
+          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, '\"');
 
-  if (self->layout)
-    {
-      if (self->ellipsize || self->wrap)
-        pango_layout_set_width (self->layout, width * PANGO_SCALE);
-      else
-        pango_layout_set_width (self->layout, -1);
+          g_free (newvalue);
+        }
+      g_string_append_c (pdata->new_str, '>');
     }
-
-  if (self->popup_menu)
-    gtk_popover_present (GTK_POPOVER (self->popup_menu));
 }
 
 static void
-gtk_label_update_cursor (GtkLabel *self)
+end_element_handler (GMarkupParseContext  *context,
+                     const char           *element_name,
+                     gpointer              user_data,
+                     GError              **error)
 {
-  GtkWidget *widget = GTK_WIDGET (self);
-
-  if (!self->select_info)
-    return;
+  UriParserData *pdata = user_data;
 
-  if (gtk_widget_is_sensitive (widget))
+  if (!strcmp (element_name, "a"))
     {
-      if (self->select_info->active_link)
-        gtk_widget_set_cursor_from_name (widget, "pointer");
-      else if (self->select_info->selectable)
-        gtk_widget_set_cursor_from_name (widget, "text");
-      else
-        gtk_widget_set_cursor (widget, NULL);
+      GtkLabelLink *link = &g_array_index (pdata->links, GtkLabelLink, pdata->links->len - 1);
+      link->end = pdata->text_len;
     }
   else
-    gtk_widget_set_cursor (widget, NULL);
+    {
+      g_string_append (pdata->new_str, "</");
+      g_string_append (pdata->new_str, element_name);
+      g_string_append_c (pdata->new_str, '>');
+    }
 }
 
 static void
-update_link_state (GtkLabel *self)
+text_handler (GMarkupParseContext  *context,
+              const char           *text,
+              gsize                 text_len,
+              gpointer              user_data,
+              GError              **error)
 {
-  GtkStateFlags state;
-  guint i;
+  UriParserData *pdata = user_data;
+  char *newtext;
 
-  if (!self->select_info)
-    return;
+  newtext = g_markup_escape_text (text, text_len);
+  g_string_append (pdata->new_str, newtext);
+  pdata->text_len += text_len;
+  g_free (newtext);
+}
 
-  for (i = 0; i < self->select_info->n_links; i++)
-    {
-      const GtkLabelLink *link = &self->select_info->links[i];
+static const GMarkupParser markup_parser =
+{
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  NULL
+};
 
-      state = gtk_widget_get_state_flags (GTK_WIDGET (self));
-      if (link->visited)
-        state |= GTK_STATE_FLAG_VISITED;
-      else
-        state |= GTK_STATE_FLAG_LINK;
-      if (link == self->select_info->active_link)
-        {
-          if (self->select_info->link_clicked)
-            state |= GTK_STATE_FLAG_ACTIVE;
-          else
-            state |= GTK_STATE_FLAG_PRELIGHT;
-        }
-      gtk_css_node_set_state (link->cssnode, state);
-    }
+static gboolean
+xml_isspace (char c)
+{
+  return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
 }
 
-static void
-gtk_label_state_flags_changed (GtkWidget     *widget,
-                               GtkStateFlags  prev_state)
+static gboolean
+parse_uri_markup (GtkLabel      *self,
+                  const char    *str,
+                  char         **new_str,
+                  GtkLabelLink  **links,
+                  guint         *out_n_links,
+                  GError       **error)
 {
-  GtkLabel *self = GTK_LABEL (widget);
-
-  if (self->select_info)
-    {
-      if (!gtk_widget_is_sensitive (widget))
-        gtk_label_select_region (self, 0, 0);
+  GMarkupParseContext *context;
+  const char *p, *end;
+  gsize length;
+  UriParserData pdata;
 
-      gtk_label_update_cursor (self);
-      update_link_state (self);
-    }
+  length = strlen (str);
+  p = str;
+  end = str + length;
 
-  if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
-    GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
-}
+  pdata.label = self;
+  pdata.links = NULL;
+  pdata.new_str = g_string_sized_new (length);
+  pdata.text_len = 0;
 
-static void 
-gtk_label_css_changed (GtkWidget         *widget,
-                       GtkCssStyleChange *change)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-  gboolean attrs_affected;
-  PangoAttrList *new_attrs = NULL;
+  while (p != end && xml_isspace (*p))
+    p++;
 
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->css_changed (widget, change);
+  context = g_markup_parse_context_new (&markup_parser, 0, &pdata, NULL);
 
-  if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_TEXT_ATTRS))
+  if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
     {
-      new_attrs = gtk_css_style_get_pango_attributes (gtk_css_style_change_get_new_style (change));
-      attrs_affected = (self->layout && pango_layout_get_attributes (self->layout)) ||
-                       new_attrs;
+      if (!g_markup_parse_context_parse (context, str, length, error))
+        goto failed;
     }
   else
-    attrs_affected = FALSE;
-
-  if (change == NULL || attrs_affected  || (self->select_info && self->select_info->links))
     {
-      gtk_label_update_layout_attributes (self, new_attrs);
+      if (!g_markup_parse_context_parse (context, "<markup>", 8, error))
+        goto failed;
 
-      if (attrs_affected)
-        gtk_widget_queue_draw (widget);
+      if (!g_markup_parse_context_parse (context, str, length, error))
+        goto failed;
+
+      if (!g_markup_parse_context_parse (context, "</markup>", 9, error))
+        goto failed;
     }
-}
 
-static PangoDirection
-get_cursor_direction (GtkLabel *self)
-{
-  GSList *l;
+  if (!g_markup_parse_context_end_parse (context, error))
+    goto failed;
 
-  g_assert (self->select_info);
+  g_markup_parse_context_free (context);
 
-  gtk_label_ensure_layout (self);
+  *new_str = g_string_free (pdata.new_str, FALSE);
 
-  for (l = pango_layout_get_lines_readonly (self->layout); l; l = l->next)
+  if (pdata.links)
     {
-      PangoLayoutLine *line = l->data;
-
-      /* If self->select_info->selection_end is at the very end of
-       * the line, we don't know if the cursor is on this line or
-       * the next without looking ahead at the next line. (End
-       * of paragraph is different from line break.) But it's
-       * definitely in this paragraph, which is good enough
-       * to figure out the resolved direction.
-       */
-       if (line->start_index + line->length >= self->select_info->selection_end)
-       return line->resolved_dir;
+      *out_n_links = pdata.links->len;
+      *links = (GtkLabelLink *)g_array_free (pdata.links, FALSE);
+    }
+  else
+    {
+      *links = NULL;
     }
 
-  return PANGO_DIRECTION_LTR;
-}
+  return TRUE;
 
-static GtkLabelLink *
-gtk_label_get_focus_link (GtkLabel *self,
-                          int      *out_index)
-{
-  GtkLabelSelectionInfo *info = self->select_info;
-  int link_index;
+failed:
+  g_markup_parse_context_free (context);
+  g_string_free (pdata.new_str, TRUE);
 
-  if (!info ||
-      info->selection_anchor != info->selection_end)
-    goto nope;
+  if (pdata.links)
+    g_array_free (pdata.links, TRUE);
+
+  return FALSE;
+}
 
-  link_index = _gtk_label_get_link_at (self, info->selection_anchor);
+static void
+gtk_label_ensure_has_tooltip (GtkLabel *self)
+{
+  guint i;
+  gboolean has_tooltip = FALSE;
 
-  if (link_index != -1)
+  for (i = 0; i < self->select_info->n_links; i++)
     {
-      if (out_index)
-        *out_index = link_index;
+      const GtkLabelLink *link = &self->select_info->links[i];
 
-      return &info->links[link_index];
+      if (link->title)
+        {
+          has_tooltip = TRUE;
+          break;
+        }
     }
 
-nope:
-  if (out_index)
-    *out_index = -1;
-  return NULL;
+  gtk_widget_set_has_tooltip (GTK_WIDGET (self), has_tooltip);
 }
 
-#define GRAPHENE_RECT_FROM_RECT(_r) (GRAPHENE_RECT_INIT ((_r)->x, (_r)->y, (_r)->width, (_r)->height))
-
+/* Reads @text and extracts the accel key, if any.
+ * @new_text will be set to the given text with the first _ removed.
+ *
+ * Returned will be the one underline attribute used for the mnemonic
+ * */
 static void
-gtk_label_snapshot (GtkWidget   *widget,
-                    GtkSnapshot *snapshot)
+extract_mnemonic_keyval (const char      *text,
+                         guint           *out_accel_key,
+                         char           **out_new_text,
+                         PangoAttribute **out_mnemonic_attribute)
 {
-  GtkLabel *self = GTK_LABEL (widget);
-  GtkLabelSelectionInfo *info;
-  GtkStyleContext *context;
-  int lx, ly;
-  int width, height;
-
-  if (!self->text || (*self->text == '\0'))
-    return;
+  const gsize text_len = strlen (text);
+  gunichar c;
+  const char *p;
 
-  gtk_label_ensure_layout (self);
+  p = text;
+  for (;;)
+    {
+      const char *_index;
 
-  context = _gtk_widget_get_style_context (widget);
-  get_layout_location (self, &lx, &ly);
+      c = g_utf8_get_char (p);
 
-  gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
+      if (c == '\0')
+        break;
 
-  info = self->select_info;
-  if (!info)
-    return;
+      if (c != '_')
+        {
+          p = g_utf8_next_char (p);
+          continue;
+        }
 
-  width = gtk_widget_get_width (widget);
-  height = gtk_widget_get_height (widget);
+      _index = p;
 
-  if (info->selection_anchor != info->selection_end)
-    {
-      int range[2];
-      cairo_region_t *range_clip;
-      cairo_rectangle_int_t clip_rect;
-      int i;
+      p = g_utf8_next_char (p);
+      c = g_utf8_get_char (p);
 
-      range[0] = MIN (info->selection_anchor, info->selection_end);
-      range[1] = MAX (info->selection_anchor, info->selection_end);
+      if (c != '_' && c != '\0')
+        {
+          const gsize byte_index = p - text - 1; /* Of the _ */
 
-      gtk_style_context_save_to_node (context, info->selection_node);
+          /* c is the accel key */
+          if (out_accel_key)
+            *out_accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
+          if (out_new_text)
+            {
+              *out_new_text = g_malloc (text_len);
+              memcpy (*out_new_text, text, byte_index);
+              memcpy (*out_new_text + byte_index, p, text_len - byte_index);
+            }
 
-      range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
-      for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
-        {
-          cairo_region_get_rectangle (range_clip, i, &clip_rect);
+          if (out_mnemonic_attribute)
+            {
+              PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
+              attr->start_index = _index - text;
+              attr->end_index = p - text;
+              *out_mnemonic_attribute = attr;
+            }
 
-          gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
-          gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
-          gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
-          gtk_snapshot_pop (snapshot);
+          return;
         }
 
-      cairo_region_destroy (range_clip);
-
-      gtk_style_context_restore (context);
+      p = g_utf8_next_char (p);
     }
-  else
+
+  /* No accel key found */
+  if (out_accel_key)
+    *out_accel_key = GDK_KEY_VoidSymbol;
+  if (out_new_text)
+    *out_new_text = NULL;
+  if (out_mnemonic_attribute)
+    *out_mnemonic_attribute = NULL;
+}
+
+static void
+gtk_label_set_markup_internal (GtkLabel    *self,
+                               const char *str,
+                               gboolean     with_uline)
+{
+  char *text = NULL;
+  GError *error = NULL;
+  PangoAttrList *attrs = NULL;
+  char *str_for_display = NULL;
+  GtkLabelLink *links = NULL;
+  guint n_links = 0;
+  PangoAttribute *mnemonic_attr = NULL;
+
+  if (!parse_uri_markup (self, str, &str_for_display, &links, &n_links, &error))
+    goto error_set;
+
+  if (links)
     {
-      GtkLabelLink *focus_link;
-      GtkLabelLink *active_link;
-      int range[2];
-      cairo_region_t *range_clip;
-      cairo_rectangle_int_t clip_rect;
-      int i;
-      GdkRectangle rect;
+      gtk_label_ensure_select_info (self);
+      self->select_info->links = g_steal_pointer (&links);
+      self->select_info->n_links = n_links;
+      gtk_label_ensure_has_tooltip (self);
+      gtk_widget_add_css_class (GTK_WIDGET (self), "link");
+    }
 
-      if (info->selectable &&
-          gtk_widget_has_focus (widget) &&
-          gtk_widget_is_drawable (widget))
+  if (!with_uline)
+   {
+no_uline:
+      /* Extract the text to display */
+      if (!pango_parse_markup (str_for_display, -1, 0, &attrs, &text, NULL, &error))
+        goto error_set;
+   }
+  else /* Underline AND markup is a little more complicated... */
+   {
+      char *new_text = NULL;
+      guint accel_keyval;
+      gboolean auto_mnemonics = TRUE;
+      gboolean do_mnemonics = self->mnemonics_visible &&
+                              (!auto_mnemonics || gtk_widget_is_sensitive (GTK_WIDGET (self))) &&
+                              (!self->mnemonic_widget || gtk_widget_is_sensitive (self->mnemonic_widget));
+
+      /* Remove the mnemonic underline */
+      extract_mnemonic_keyval (str_for_display,
+                               &accel_keyval,
+                               &new_text,
+                               NULL);
+      if (!new_text) /* No underline found anyway */
+        goto no_uline;
+
+      self->mnemonic_keyval = accel_keyval;
+
+      /* Extract the text to display */
+      if (!pango_parse_markup (new_text, -1, '_',
+                               do_mnemonics ? &attrs : NULL, &text, NULL, &error))
+        goto error_set;
+
+      if (do_mnemonics)
         {
-          PangoDirection cursor_direction;
+          /* text is now the final text, but we need to parse str_for_display once again
+           * *with* the mnemonic underline so we can remove the markup tags and get the
+           * proper attribute indices */
+          char *text_for_accel;
 
-          cursor_direction = get_cursor_direction (self);
-          gtk_snapshot_render_insertion_cursor (snapshot, context,
-                                                lx, ly,
-                                                self->layout, self->select_info->selection_end,
-                                                cursor_direction);
+          if (!pango_parse_markup (str_for_display, -1, 0, NULL, &text_for_accel, NULL, &error))
+            {
+              g_free (new_text);
+              goto error_set;
+            }
+
+          extract_mnemonic_keyval (text_for_accel,
+                                   NULL,
+                                   NULL,
+                                   &mnemonic_attr);
+          g_free (text_for_accel);
         }
 
-      focus_link = gtk_label_get_focus_link (self, NULL);
-      active_link = info->active_link;
+      g_free (new_text);
+   }
 
-      if (active_link)
-        {
-          range[0] = active_link->start;
-          range[1] = active_link->end;
+  g_free (str_for_display);
 
-          gtk_style_context_save_to_node (context, active_link->cssnode);
+  if (text)
+    gtk_label_set_text_internal (self, text);
 
-          range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
-          for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
-            {
-              cairo_region_get_rectangle (range_clip, i, &clip_rect);
+  g_clear_pointer (&self->markup_attrs, pango_attr_list_unref);
+  self->markup_attrs = attrs;
 
-              gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
-              gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
-              gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
-              gtk_snapshot_pop (snapshot);
-            }
+  if (mnemonic_attr)
+    pango_attr_list_insert_before (self->markup_attrs, mnemonic_attr);
 
-          cairo_region_destroy (range_clip);
+  return;
 
-          gtk_style_context_restore (context);
-        }
+error_set:
+  g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
+             str, error->message);
+  g_error_free (error);
 
-      if (focus_link && gtk_widget_has_visible_focus (widget))
-        {
-          range[0] = focus_link->start;
-          range[1] = focus_link->end;
+}
+
+/**
+ * gtk_label_set_markup:
+ * @self: a #GtkLabel
+ * @str: a markup string (see [Pango markup format][PangoMarkupFormat])
+ *
+ * Parses @str which is marked up with the
+ * [Pango text markup language][PangoMarkupFormat], setting the
+ * label’s text and attribute list based on the parse results.
+ *
+ * If the @str is external data, you may need to escape it with
+ * g_markup_escape_text() or g_markup_printf_escaped():
+ *
+ * |[<!-- language="C" -->
+ * GtkWidget *self = gtk_label_new (NULL);
+ * const char *str = "...";
+ * const char *format = "<span style=\"italic\">\%s</span>";
+ * char *markup;
+ *
+ * markup = g_markup_printf_escaped (format, str);
+ * gtk_label_set_markup (GTK_LABEL (self), markup);
+ * g_free (markup);
+ * ]|
+ *
+ * This function will set the #GtkLabel:use-markup property to %TRUE as
+ * a side effect.
+ *
+ * If you set the label contents using the #GtkLabel:label property you
+ * should also ensure that you set the #GtkLabel:use-markup property
+ * accordingly.
+ *
+ * See also: gtk_label_set_text()
+ **/
+void
+gtk_label_set_markup (GtkLabel    *self,
+                      const char *str)
+{
+  gboolean changed;
 
-          gtk_style_context_save_to_node (context, focus_link->cssnode);
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-          range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
-          cairo_region_get_extents (range_clip, &rect);
+  g_object_freeze_notify (G_OBJECT (self));
 
-          gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
+  changed = gtk_label_set_label_internal (self, str);
+  changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
+  changed = gtk_label_set_use_underline_internal (self, FALSE) || changed;
 
-          cairo_region_destroy (range_clip);
+  if (changed)
+    gtk_label_recalculate (self);
 
-          gtk_style_context_restore (context);
-        }
-    }
+  g_object_thaw_notify (G_OBJECT (self));
 }
 
 /**
- * gtk_label_set_text_with_mnemonic:
+ * gtk_label_set_markup_with_mnemonic:
  * @self: a #GtkLabel
- * @str: a string
+ * @str: a markup string (see
+ *     [Pango markup format][PangoMarkupFormat])
  *
- * Sets the label’s text from the string @str.
+ * Parses @str which is marked up with the
+ * [Pango text markup language][PangoMarkupFormat],
+ * setting the label’s text and attribute list based on the parse results.
  * If characters in @str are preceded by an underscore, they are underlined
  * indicating that they represent a keyboard accelerator called a mnemonic.
+ *
  * The mnemonic key can be used to activate another widget, chosen
  * automatically, or explicitly using gtk_label_set_mnemonic_widget().
- **/
+ */
 void
-gtk_label_set_text_with_mnemonic (GtkLabel    *self,
-                                  const char *str)
+gtk_label_set_markup_with_mnemonic (GtkLabel    *self,
+                                    const char *str)
 {
   gboolean changed;
 
   g_return_if_fail (GTK_IS_LABEL (self));
-  g_return_if_fail (str != NULL);
 
   g_object_freeze_notify (G_OBJECT (self));
 
   changed = gtk_label_set_label_internal (self, str);
-  changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
+  changed = gtk_label_set_use_markup_internal (self, TRUE) || changed;
   changed = gtk_label_set_use_underline_internal (self, TRUE) || changed;
 
   if (changed)
@@ -3567,344 +3611,433 @@ gtk_label_set_text_with_mnemonic (GtkLabel    *self,
   g_object_thaw_notify (G_OBJECT (self));
 }
 
-static void
-gtk_label_unrealize (GtkWidget *widget)
+/**
+ * gtk_label_get_text:
+ * @self: a #GtkLabel
+ *
+ * Fetches the text from a label widget, as displayed on the
+ * screen. This does not include any embedded underlines
+ * indicating mnemonics or Pango markup. (See gtk_label_get_label())
+ *
+ * Returns: the text in the label widget. This is the internal
+ *   string used by the label, and must not be modified.
+ **/
+const char *
+gtk_label_get_text (GtkLabel *self)
 {
-  GtkLabel *self = GTK_LABEL (widget);
+  g_return_val_if_fail (GTK_IS_LABEL (self), NULL);
 
-  if (self->select_info &&
-      self->select_info->provider)
+  return self->text;
+}
+
+/**
+ * gtk_label_set_justify:
+ * @self: a #GtkLabel
+ * @jtype: a #GtkJustification
+ *
+ * Sets the alignment of the lines in the text of the label relative to
+ * each other. %GTK_JUSTIFY_LEFT is the default value when the widget is
+ * first created with gtk_label_new(). If you instead want to set the
+ * alignment of the label as a whole, use gtk_widget_set_halign() instead.
+ * gtk_label_set_justify() has no effect on labels containing only a
+ * single line.
+ */
+void
+gtk_label_set_justify (GtkLabel        *self,
+                       GtkJustification jtype)
+{
+  g_return_if_fail (GTK_IS_LABEL (self));
+  g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
+
+  if ((GtkJustification) self->jtype != jtype)
     {
-      GdkClipboard *clipboard = gtk_widget_get_primary_clipboard (widget);
+      self->jtype = jtype;
 
-      if (gdk_clipboard_get_content (clipboard) == self->select_info->provider)
-        gdk_clipboard_set_content (clipboard, NULL);
+      /* No real need to be this drastic, but easier than duplicating the code */
+      gtk_label_clear_layout (self);
+
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_JUSTIFY]);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
     }
+}
 
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
+/**
+ * gtk_label_get_justify:
+ * @self: a #GtkLabel
+ *
+ * Returns the justification of the label. See gtk_label_set_justify().
+ *
+ * Returns: #GtkJustification
+ **/
+GtkJustification
+gtk_label_get_justify (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), 0);
+
+  return self->jtype;
 }
 
-static gboolean
-get_layout_index (GtkLabel *self,
-                  int       x,
-                  int       y,
-                  int      *index)
+/**
+ * gtk_label_set_ellipsize:
+ * @self: a #GtkLabel
+ * @mode: a #PangoEllipsizeMode
+ *
+ * Sets the mode used to ellipsize (add an ellipsis: "...") to the text
+ * if there is not enough space to render the entire string.
+ **/
+void
+gtk_label_set_ellipsize (GtkLabel          *self,
+                         PangoEllipsizeMode mode)
 {
-  int trailing = 0;
-  const char *cluster;
-  const char *cluster_end;
-  gboolean inside;
-  int lx, ly;
+  g_return_if_fail (GTK_IS_LABEL (self));
+  g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
 
-  *index = 0;
+  if ((PangoEllipsizeMode) self->ellipsize != mode)
+    {
+      self->ellipsize = mode;
 
-  gtk_label_ensure_layout (self);
-  get_layout_location (self, &lx, &ly);
+      /* No real need to be this drastic, but easier than duplicating the code */
+      gtk_label_clear_layout (self);
 
-  /* Translate x/y to layout position */
-  x -= lx;
-  y -= ly;
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_ELLIPSIZE]);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
+    }
+}
 
-  x *= PANGO_SCALE;
-  y *= PANGO_SCALE;
+/**
+ * gtk_label_get_ellipsize:
+ * @self: a #GtkLabel
+ *
+ * Returns the ellipsizing position of the label. See gtk_label_set_ellipsize().
+ *
+ * Returns: #PangoEllipsizeMode
+ **/
+PangoEllipsizeMode
+gtk_label_get_ellipsize (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), PANGO_ELLIPSIZE_NONE);
 
-  inside = pango_layout_xy_to_index (self->layout,
-                                     x, y,
-                                     index, &trailing);
+  return self->ellipsize;
+}
 
-  cluster = self->text + *index;
-  cluster_end = cluster;
-  while (trailing)
+/**
+ * gtk_label_set_width_chars:
+ * @self: a #GtkLabel
+ * @n_chars: the new desired width, in characters.
+ *
+ * Sets the desired width in characters of @label to @n_chars.
+ **/
+void
+gtk_label_set_width_chars (GtkLabel *self,
+                           int       n_chars)
+{
+  g_return_if_fail (GTK_IS_LABEL (self));
+
+  if (self->width_chars != n_chars)
     {
-      cluster_end = g_utf8_next_char (cluster_end);
-      --trailing;
+      self->width_chars = n_chars;
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WIDTH_CHARS]);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
     }
+}
 
-  *index += (cluster_end - cluster);
+/**
+ * gtk_label_get_width_chars:
+ * @self: a #GtkLabel
+ *
+ * Retrieves the desired width of @label, in characters. See
+ * gtk_label_set_width_chars().
+ *
+ * Returns: the width of the label in characters.
+ **/
+int
+gtk_label_get_width_chars (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), -1);
+
+  return self->width_chars;
+}
+
+/**
+ * gtk_label_set_max_width_chars:
+ * @self: a #GtkLabel
+ * @n_chars: the new desired maximum width, in characters.
+ *
+ * Sets the desired maximum width in characters of @label to @n_chars.
+ **/
+void
+gtk_label_set_max_width_chars (GtkLabel *self,
+                               int       n_chars)
+{
+  g_return_if_fail (GTK_IS_LABEL (self));
+
+  if (self->max_width_chars != n_chars)
+    {
+      self->max_width_chars = n_chars;
+
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_MAX_WIDTH_CHARS]);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
+    }
+}
+
+/**
+ * gtk_label_get_max_width_chars:
+ * @self: a #GtkLabel
+ *
+ * Retrieves the desired maximum width of @label, in characters. See
+ * gtk_label_set_width_chars().
+ *
+ * Returns: the maximum width of the label in characters.
+ **/
+int
+gtk_label_get_max_width_chars (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), -1);
 
-  return inside;
+  return self->max_width_chars;
 }
 
-static gboolean
-range_is_in_ellipsis_full (GtkLabel *self,
-                           int       range_start,
-                           int       range_end,
-                           int      *ellipsis_start,
-                           int      *ellipsis_end)
+/**
+ * gtk_label_set_wrap:
+ * @self: a #GtkLabel
+ * @wrap: the setting
+ *
+ * Toggles line wrapping within the #GtkLabel widget. %TRUE makes it break
+ * lines if text exceeds the widget’s size. %FALSE lets the text get cut off
+ * by the edge of the widget if it exceeds the widget size.
+ *
+ * Note that setting line wrapping to %TRUE does not make the label
+ * wrap at its parent container’s width, because GTK widgets
+ * conceptually can’t make their requisition depend on the parent
+ * container’s size. For a label that wraps at a specific position,
+ * set the label’s width using gtk_widget_set_size_request().
+ **/
+void
+gtk_label_set_wrap (GtkLabel *self,
+                    gboolean  wrap)
 {
-  PangoLayoutIter *iter;
-  gboolean in_ellipsis;
-
-  if (!self->ellipsize)
-    return FALSE;
-
-  gtk_label_ensure_layout (self);
-
-  if (!pango_layout_is_ellipsized (self->layout))
-    return FALSE;
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-  iter = pango_layout_get_iter (self->layout);
+  wrap = wrap != FALSE;
 
-  in_ellipsis = FALSE;
+  if (self->wrap != wrap)
+    {
+      self->wrap = wrap;
 
-  do {
-    PangoLayoutRun *run;
+      gtk_label_clear_layout (self);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP]);
+    }
+}
 
-    run = pango_layout_iter_get_run_readonly (iter);
-    if (run)
-      {
-        PangoItem *item;
+/**
+ * gtk_label_get_wrap:
+ * @self: a #GtkLabel
+ *
+ * Returns whether lines in the label are automatically wrapped.
+ * See gtk_label_set_wrap().
+ *
+ * Returns: %TRUE if the lines of the label are automatically wrapped.
+ */
+gboolean
+gtk_label_get_wrap (GtkLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
 
-        item = ((PangoGlyphItem*)run)->item;
+  return self->wrap;
+}
 
-        if (item->offset <= range_start && range_end <= item->offset + item->length)
-          {
-            if (item->analysis.flags & PANGO_ANALYSIS_FLAG_IS_ELLIPSIS)
-              {
-                if (ellipsis_start)
-                  *ellipsis_start = item->offset;
-                if (ellipsis_end)
-                  *ellipsis_end = item->offset + item->length;
-                in_ellipsis = TRUE;
-              }
-            break;
-          }
-        else if (item->offset + item->length >= range_end)
-          break;
-      }
-  } while (pango_layout_iter_next_run (iter));
+/**
+ * gtk_label_set_wrap_mode:
+ * @self: a #GtkLabel
+ * @wrap_mode: the line wrapping mode
+ *
+ * If line wrapping is on (see gtk_label_set_wrap()) this controls how
+ * the line wrapping is done. The default is %PANGO_WRAP_WORD which means
+ * wrap on word boundaries.
+ **/
+void
+gtk_label_set_wrap_mode (GtkLabel *self,
+                         PangoWrapMode wrap_mode)
+{
+  g_return_if_fail (GTK_IS_LABEL (self));
 
-  pango_layout_iter_free (iter);
+  if (self->wrap_mode != wrap_mode)
+    {
+      self->wrap_mode = wrap_mode;
+      g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_WRAP_MODE]);
 
-  return in_ellipsis;
+      gtk_widget_queue_resize (GTK_WIDGET (self));
+    }
 }
 
-static gboolean
-range_is_in_ellipsis (GtkLabel *self,
-                      int       range_start,
-                      int       range_end)
+/**
+ * gtk_label_get_wrap_mode:
+ * @self: a #GtkLabel
+ *
+ * Returns line wrap mode used by the label. See gtk_label_set_wrap_mode().
+ *
+ * Returns: %TRUE if the lines of the label are automatically wrapped.
+ */
+PangoWrapMode
+gtk_label_get_wrap_mode (GtkLabel *self)
 {
-  return range_is_in_ellipsis_full (self, range_start, range_end, NULL, NULL);
+  g_return_val_if_fail (GTK_IS_LABEL (self), FALSE);
+
+  return self->wrap_mode;
 }
 
 static void
-gtk_label_select_word (GtkLabel *self)
+gtk_label_clear_layout (GtkLabel *self)
 {
-  int min, max;
-
-  int start_index = gtk_label_move_backward_word (self, self->select_info->selection_end);
-  int end_index = gtk_label_move_forward_word (self, self->select_info->selection_end);
-
-  min = MIN (self->select_info->selection_anchor,
-            self->select_info->selection_end);
-  max = MAX (self->select_info->selection_anchor,
-            self->select_info->selection_end);
-
-  min = MIN (min, start_index);
-  max = MAX (max, end_index);
-
-  gtk_label_select_region_index (self, min, max);
+  g_clear_object (&self->layout);
 }
 
-static gboolean
-gtk_label_grab_focus (GtkWidget *widget)
+static void
+gtk_label_ensure_layout (GtkLabel *self)
 {
-  GtkLabel *self = GTK_LABEL (widget);
-  gboolean select_on_focus;
-  GtkWidget *prev_focus;
+  PangoAlignment align;
+  gboolean rtl;
 
-  if (self->select_info == NULL)
-    return FALSE;
+  if (self->layout)
+    return;
 
-  prev_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
+  align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+  rtl = _gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
+  self->layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), self->text);
 
-  if (!GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget))
-    return FALSE;
+  gtk_label_update_layout_attributes (self, NULL);
 
-  if (self->select_info->selectable)
+  switch (self->jtype)
     {
-      g_object_get (gtk_widget_get_settings (widget),
-                    "gtk-label-select-on-focus",
-                    &select_on_focus,
-                    NULL);
-
-      if (select_on_focus && !self->in_click &&
-          !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
-        gtk_label_select_region (self, 0, -1);
+    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;
+    case GTK_JUSTIFY_FILL:
+      align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+      pango_layout_set_justify (self->layout, TRUE);
+      break;
+    default:
+      g_assert_not_reached();
     }
-  else
-    {
-      if (self->select_info->links && !self->in_click &&
-          !(prev_focus && gtk_widget_is_ancestor (prev_focus, widget)))
-        {
-          guint i;
-
-          for (i = 0; i < self->select_info->n_links; i++)
-            {
-              const GtkLabelLink *link = &self->select_info->links[i];
 
-              if (!range_is_in_ellipsis (self, link->start, link->end))
-                {
-                  self->select_info->selection_anchor = link->start;
-                  self->select_info->selection_end = link->start;
-                  break;
-                }
-            }
-        }
-    }
+  pango_layout_set_alignment (self->layout, align);
+  pango_layout_set_ellipsize (self->layout, self->ellipsize);
+  pango_layout_set_wrap (self->layout, self->wrap_mode);
+  pango_layout_set_single_paragraph_mode (self->layout, self->single_line_mode);
+  if (self->lines > 0)
+    pango_layout_set_height (self->layout, - self->lines);
 
-  return TRUE;
+  if (self->ellipsize || self->wrap)
+    pango_layout_set_width (self->layout, gtk_widget_get_width (GTK_WIDGET (self)) * PANGO_SCALE);
 }
 
-static gboolean
-gtk_label_focus (GtkWidget        *widget,
-                 GtkDirectionType  direction)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-  GtkLabelSelectionInfo *info = self->select_info;
-  GtkLabelLink *focus_link;
-
-  if (!gtk_widget_is_focus (widget))
-    {
-      gtk_widget_grab_focus (widget);
-      if (info)
-        {
-          focus_link = gtk_label_get_focus_link (self, NULL);
-          if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
-            {
-              int i;
-              for (i = info->n_links - 1; i >= 0; i--)
-                {
-                  focus_link = &info->links[i];
-                  if (!range_is_in_ellipsis (self, focus_link->start, focus_link->end))
-                    {
-                      info->selection_anchor = focus_link->start;
-                      info->selection_end = focus_link->start;
-                    }
-                }
-            }
+/**
+ * gtk_label_set_text_with_mnemonic:
+ * @self: a #GtkLabel
+ * @str: a string
+ *
+ * Sets the label’s text from the string @str.
+ * If characters in @str are preceded by an underscore, they are underlined
+ * indicating that they represent a keyboard accelerator called a mnemonic.
+ * The mnemonic key can be used to activate another widget, chosen
+ * automatically, or explicitly using gtk_label_set_mnemonic_widget().
+ **/
+void
+gtk_label_set_text_with_mnemonic (GtkLabel    *self,
+                                  const char *str)
+{
+  gboolean changed;
 
-          return TRUE;
-        }
+  g_return_if_fail (GTK_IS_LABEL (self));
+  g_return_if_fail (str != NULL);
 
-      return FALSE;
-    }
+  g_object_freeze_notify (G_OBJECT (self));
 
-  if (!info)
-    return FALSE;
+  changed = gtk_label_set_label_internal (self, str);
+  changed = gtk_label_set_use_markup_internal (self, FALSE) || changed;
+  changed = gtk_label_set_use_underline_internal (self, TRUE) || changed;
 
-  if (info->selectable)
-    {
-      int index;
+  if (changed)
+    gtk_label_recalculate (self);
 
-      if (info->selection_anchor != info->selection_end)
-        goto out;
+  g_object_thaw_notify (G_OBJECT (self));
+}
 
-      index = info->selection_anchor;
+static int
+gtk_label_move_forward_word (GtkLabel *self,
+                             int       start)
+{
+  int new_pos = g_utf8_pointer_to_offset (self->text, self->text + start);
+  int length;
 
-      if (direction == GTK_DIR_TAB_FORWARD)
-        {
-          guint i;
-          for (i = 0; i < info->n_links; i++)
-            {
-              const GtkLabelLink *link = &info->links[i];
+  length = g_utf8_strlen (self->text, -1);
+  if (new_pos < length)
+    {
+      const PangoLogAttr *log_attrs;
+      int n_attrs;
 
-              if (link->start > index)
-                {
-                  if (!range_is_in_ellipsis (self, link->start, link->end))
-                    {
-                      gtk_label_select_region_index (self, link->start, link->start);
-                      return TRUE;
-                    }
-                }
-            }
-        }
-      else if (direction == GTK_DIR_TAB_BACKWARD)
-        {
-          int i;
-          for (i = info->n_links - 1; i >= 0; i--)
-            {
-              GtkLabelLink *link = &info->links[i];
+      gtk_label_ensure_layout (self);
 
-              if (link->end < index)
-                {
-                  if (!range_is_in_ellipsis (self, link->start, link->end))
-                    {
-                      gtk_label_select_region_index (self, link->start, link->start);
-                      return TRUE;
-                    }
-                }
-            }
-        }
+      log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
 
-      goto out;
+      /* Find the next word end */
+      new_pos++;
+      while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
+        new_pos++;
     }
-  else
-    {
-      int focus_link_index;
-      int new_index = -1;
-      int i;
 
-      if (info->n_links == 0)
-        goto out;
+  return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+}
 
-      focus_link = gtk_label_get_focus_link (self, &focus_link_index);
+static int
+gtk_label_move_backward_word (GtkLabel *self,
+                              int       start)
+{
+  int new_pos = g_utf8_pointer_to_offset (self->text, self->text + start);
 
-      if (!focus_link)
-        goto out;
+  if (new_pos > 0)
+    {
+      const PangoLogAttr *log_attrs;
+      int n_attrs;
 
-      switch (direction)
-        {
-        case GTK_DIR_TAB_FORWARD:
-          if (focus_link)
-            new_index = (focus_link_index + 1) % info->n_links;
-          else
-            new_index = 0;
+      gtk_label_ensure_layout (self);
 
-          for (i = new_index; i < info->n_links; i++)
-            {
-              const GtkLabelLink *link = &info->links[i];
-              if (!range_is_in_ellipsis (self, link->start, link->end))
-                break;
-            }
-          break;
+      log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
 
-        case GTK_DIR_TAB_BACKWARD:
-          if (focus_link)
-            new_index = focus_link_index == 0 ? info->n_links  - 1 : focus_link_index - 1;
-          else
-            new_index = info->n_links - 1;
+      new_pos -= 1;
 
-          for (i = new_index; i >= 0; i--)
-            {
-              const GtkLabelLink *link = &info->links[i];
-              if (!range_is_in_ellipsis (self, link->start, link->end))
-                break;
-            }
-          break;
+      /* Find the previous word beginning */
+      while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
+        new_pos--;
+    }
 
-        default:
-        case GTK_DIR_UP:
-        case GTK_DIR_DOWN:
-        case GTK_DIR_LEFT:
-        case GTK_DIR_RIGHT:
-          goto out;
-        }
+  return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+}
 
-      if (new_index != -1)
-        {
-          focus_link = &info->links[new_index];
-          info->selection_anchor = focus_link->start;
-          info->selection_end = focus_link->start;
-          gtk_widget_queue_draw (widget);
+static void
+gtk_label_select_word (GtkLabel *self)
+{
+  int min, max;
 
-          return TRUE;
-        }
-    }
+  int start_index = gtk_label_move_backward_word (self, self->select_info->selection_end);
+  int end_index = gtk_label_move_forward_word (self, self->select_info->selection_end);
 
-out:
+  min = MIN (self->select_info->selection_anchor,
+             self->select_info->selection_end);
+  max = MAX (self->select_info->selection_anchor,
+             self->select_info->selection_end);
 
-  return FALSE;
+  min = MIN (min, start_index);
+  max = MAX (max, end_index);
+
+  gtk_label_select_region_index (self, min, max);
 }
 
 static void
@@ -4139,7 +4272,7 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
   if (info->in_drag)
     {
       if (gtk_drag_check_threshold (widget, info->drag_start_x, info->drag_start_y, x, y))
-       {
+        {
           GdkDrag *drag;
           GdkSurface *surface;
           GdkDevice *device;
@@ -4157,9 +4290,8 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
           gtk_drag_icon_set_from_paintable (drag, get_selection_paintable (self), 0, 0);
 
           g_object_unref (drag);
-          
-         info->in_drag = FALSE;
-       }
+          info->in_drag = FALSE;
+        }
     }
   else
     {
@@ -4211,6 +4343,34 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
     }
 }
 
+static void
+gtk_label_update_actions (GtkLabel *self)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+  gboolean has_selection;
+  GtkLabelLink *link;
+
+  if (self->select_info)
+    {
+      has_selection = self->select_info->selection_anchor != self->select_info->selection_end;
+      link = self->select_info->active_link;
+    }
+  else
+    {
+      has_selection = FALSE;
+      link = gtk_label_get_focus_link (self, NULL);
+    }
+
+  gtk_widget_action_set_enabled (widget, "clipboard.cut", FALSE);
+  gtk_widget_action_set_enabled (widget, "clipboard.copy", has_selection);
+  gtk_widget_action_set_enabled (widget, "clipboard.paste", FALSE);
+  gtk_widget_action_set_enabled (widget, "selection.select-all",
+                                 gtk_label_get_selectable (self));
+  gtk_widget_action_set_enabled (widget, "selection.delete", FALSE);
+  gtk_widget_action_set_enabled (widget, "link.open", !has_selection && link);
+  gtk_widget_action_set_enabled (widget, "link.copy", !has_selection && link);
+}
+
 static void
 gtk_label_update_active_link (GtkWidget *widget,
                               double     x,
@@ -4530,9 +4690,9 @@ gtk_label_set_selectable (GtkLabel *self,
 /**
  * gtk_label_get_selectable:
  * @self: a #GtkLabel
- * 
+ *
  * Gets the value set by gtk_label_set_selectable().
- * 
+ *
  * Returns: %TRUE if the user can copy text from the label
  **/
 gboolean
@@ -4678,10 +4838,10 @@ gtk_label_select_region  (GtkLabel *self,
     {
       if (start_offset < 0)
         start_offset = g_utf8_strlen (self->text, -1);
-      
+
       if (end_offset < 0)
         end_offset = g_utf8_strlen (self->text, -1);
-      
+
       gtk_label_select_region_index (self,
                                      g_utf8_offset_to_pointer (self->text, start_offset) - self->text,
                                      g_utf8_offset_to_pointer (self->text, end_offset) - self->text);
@@ -4693,10 +4853,10 @@ gtk_label_select_region  (GtkLabel *self,
  * @self: a #GtkLabel
  * @start: (out): return location for start of selection, as a character offset
  * @end: (out): return location for end of selection, as a character offset
- * 
+ *
  * Gets the selected range of characters in the label, returning %TRUE
  * if there’s a selection.
- * 
+ *
  * Returns: %TRUE if selection is non-empty
  **/
 gboolean
@@ -4721,7 +4881,7 @@ gtk_label_get_selection_bounds (GtkLabel  *self,
       int start_index, end_index;
       int start_offset, end_offset;
       int len;
-      
+
       start_index = MIN (self->select_info->selection_anchor,
                    self->select_info->selection_end);
       end_index = MAX (self->select_info->selection_anchor,
@@ -4734,7 +4894,7 @@ gtk_label_get_selection_bounds (GtkLabel  *self,
 
       if (start_index > len)
         start_index = len;
-      
+
       start_offset = g_utf8_strlen (self->text, start_index);
       end_offset = g_utf8_strlen (self->text, end_index);
 
@@ -4744,7 +4904,7 @@ gtk_label_get_selection_bounds (GtkLabel  *self,
           start_offset = end_offset;
           end_offset = tmp;
         }
-      
+
       if (start)
         *start = start_offset;
 
@@ -4759,7 +4919,7 @@ gtk_label_get_selection_bounds (GtkLabel  *self,
 /**
  * gtk_label_get_layout:
  * @self: a #GtkLabel
- * 
+ *
  * Gets the #PangoLayout used to display the label.
  * The layout is useful to e.g. convert text positions to
  * pixel positions, in combination with gtk_label_get_layout_offsets().
@@ -4989,11 +5149,10 @@ get_better_cursor (GtkLabel *self,
 
 static int
 gtk_label_move_logically (GtkLabel *self,
-                         int       start,
-                         int       count)
+                          int       start,
+                          int       count)
 {
-  int offset = g_utf8_pointer_to_offset (self->text,
-                                         self->text + start);
+  int offset = g_utf8_pointer_to_offset (self->text, self->text + start);
 
   if (self->text)
     {
@@ -5008,21 +5167,21 @@ gtk_label_move_logically (GtkLabel *self,
       log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
 
       while (count > 0 && offset < length)
-       {
-         do
-           offset++;
-         while (offset < length && !log_attrs[offset].is_cursor_position);
-         
-         count--;
-       }
+        {
+          do
+            offset++;
+          while (offset < length && !log_attrs[offset].is_cursor_position);
+
+          count--;
+        }
       while (count < 0 && offset > 0)
-       {
-         do
-           offset--;
-         while (offset > 0 && !log_attrs[offset].is_cursor_position);
-         
-         count++;
-       }
+        {
+          do
+            offset--;
+          while (offset > 0 && !log_attrs[offset].is_cursor_position);
+
+          count++;
+        }
     }
 
   return g_utf8_offset_to_pointer (self->text, offset) - self->text;
@@ -5030,13 +5189,13 @@ gtk_label_move_logically (GtkLabel *self,
 
 static int
 gtk_label_move_visually (GtkLabel *self,
-                        int       start,
-                        int       count)
+                         int       start,
+                         int       count)
 {
   int index;
 
   index = start;
-  
+
   while (count != 0)
     {
       int new_index, new_trailing;
@@ -5046,8 +5205,8 @@ gtk_label_move_visually (GtkLabel *self,
       gtk_label_ensure_layout (self);
 
       g_object_get (gtk_widget_get_settings (GTK_WIDGET (self)),
-                   "gtk-split-cursor", &split_cursor,
-                   NULL);
+                    "gtk-split-cursor", &split_cursor,
+                    NULL);
 
       if (split_cursor)
         strong = TRUE;
@@ -5071,80 +5230,26 @@ gtk_label_move_visually (GtkLabel *self,
         }
 
       if (count > 0)
-       {
-         pango_layout_move_cursor_visually (self->layout, strong, index, 0, 1, &new_index, &new_trailing);
-         count--;
-       }
+        {
+          pango_layout_move_cursor_visually (self->layout, strong, index, 0, 1, &new_index, &new_trailing);
+          count--;
+        }
       else
-       {
-         pango_layout_move_cursor_visually (self->layout, strong, index, 0, -1, &new_index, &new_trailing);
-         count++;
-       }
+        {
+          pango_layout_move_cursor_visually (self->layout, strong, index, 0, -1, &new_index, &new_trailing);
+          count++;
+        }
 
       if (new_index < 0 || new_index == G_MAXINT)
-       break;
-
-      index = new_index;
-      
-      while (new_trailing--)
-       index = g_utf8_next_char (self->text + new_index) - self->text;
-    }
-  
-  return index;
-}
-
-static int
-gtk_label_move_forward_word (GtkLabel *self,
-                            int       start)
-{
-  int new_pos = g_utf8_pointer_to_offset (self->text,
-                                          self->text + start);
-  int length;
-
-  length = g_utf8_strlen (self->text, -1);
-  if (new_pos < length)
-    {
-      const PangoLogAttr *log_attrs;
-      int n_attrs;
-
-      gtk_label_ensure_layout (self);
-
-      log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
-
-      /* Find the next word end */
-      new_pos++;
-      while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
-        new_pos++;
-    }
-
-  return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
-}
-
-
-static int
-gtk_label_move_backward_word (GtkLabel *self,
-                             int       start)
-{
-  int new_pos = g_utf8_pointer_to_offset (self->text,
-                                          self->text + start);
-
-  if (new_pos > 0)
-    {
-      const PangoLogAttr *log_attrs;
-      int n_attrs;
-
-      gtk_label_ensure_layout (self);
-
-      log_attrs = pango_layout_get_log_attrs_readonly (self->layout, &n_attrs);
+        break;
 
-      new_pos -= 1;
+      index = new_index;
 
-      /* Find the previous word beginning */
-      while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
-        new_pos--;
+      while (new_trailing--)
+        index = g_utf8_next_char (self->text + new_index) - self->text;
     }
 
-  return g_utf8_offset_to_pointer (self->text, new_pos) - self->text;
+  return index;
 }
 
 static void
@@ -5275,135 +5380,6 @@ gtk_label_move_cursor (GtkLabel       *self,
     gtk_label_select_region_index (self, new_pos, new_pos);
 }
 
-static void
-gtk_label_copy_clipboard (GtkLabel *self)
-{
-  if (self->text && self->select_info)
-    {
-      int start, end;
-      int len;
-      GdkClipboard *clipboard;
-
-      start = MIN (self->select_info->selection_anchor,
-                   self->select_info->selection_end);
-      end = MAX (self->select_info->selection_anchor,
-                 self->select_info->selection_end);
-
-      len = strlen (self->text);
-
-      if (end > len)
-        end = len;
-
-      if (start > len)
-        start = len;
-
-      clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self));
-
-      if (start != end)
-        {
-          char *str = g_strndup (self->text + start, end - start);
-         gdk_clipboard_set_text (clipboard, str);
-          g_free (str);
-        }
-      else
-        {
-          GtkLabelLink *link;
-
-          link = gtk_label_get_focus_link (self, NULL);
-          if (link)
-            gdk_clipboard_set_text (clipboard, link->uri);
-        }
-    }
-}
-
-static void
-gtk_label_select_all (GtkLabel *self)
-{
-  gtk_label_select_region_index (self, 0, strlen (self->text));
-}
-
-static void
-gtk_label_activate_link_open (GtkWidget  *widget,
-                              const char *name,
-                              GVariant   *parameter)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-  GtkLabelLink *link = self->select_info->context_link;
-
-  if (link)
-    emit_activate_link (self, link);
-}
-
-static void
-gtk_label_activate_link_copy (GtkWidget  *widget,
-                              const char *name,
-                              GVariant   *parameter)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-  GtkLabelLink *link = self->select_info->context_link;
-
-  if (link)
-    {
-      GdkClipboard *clipboard;
-
-      clipboard = gtk_widget_get_clipboard (widget);
-      gdk_clipboard_set_text (clipboard, link->uri);
-    }
-  else
-    g_print ("no link ?!\n");
-}
-
-static void
-gtk_label_activate_clipboard_copy (GtkWidget  *widget,
-                                   const char *name,
-                                   GVariant   *parameter)
-{
-  g_signal_emit_by_name (widget, "copy-clipboard");
-}
-
-static void
-gtk_label_activate_selection_select_all (GtkWidget  *widget,
-                                         const char *name,
-                                         GVariant   *parameter)
-{
-  gtk_label_select_all (GTK_LABEL (widget));
-}
-
-static void
-gtk_label_nop (GtkWidget  *widget,
-               const char *name,
-               GVariant   *parameter)
-{
-}
-
-static void
-gtk_label_update_actions (GtkLabel *self)
-{
-  GtkWidget *widget = GTK_WIDGET (self);
-  gboolean has_selection;
-  GtkLabelLink *link;
-
-  if (self->select_info)
-    {
-      has_selection = self->select_info->selection_anchor != self->select_info->selection_end;
-      link = self->select_info->active_link;
-    }
-  else
-    {
-      has_selection = FALSE;
-      link = gtk_label_get_focus_link (self, NULL);
-    }
-
-  gtk_widget_action_set_enabled (widget, "clipboard.cut", FALSE);
-  gtk_widget_action_set_enabled (widget, "clipboard.copy", has_selection);
-  gtk_widget_action_set_enabled (widget, "clipboard.paste", FALSE);
-  gtk_widget_action_set_enabled (widget, "selection.select-all",
-                                 gtk_label_get_selectable (self));
-  gtk_widget_action_set_enabled (widget, "selection.delete", FALSE);
-  gtk_widget_action_set_enabled (widget, "link.open", !has_selection && link);
-  gtk_widget_action_set_enabled (widget, "link.copy", !has_selection && link);
-}
-
 static GMenuModel *
 gtk_label_get_menu_model (GtkLabel *self)
 {
@@ -5484,82 +5460,6 @@ gtk_label_do_popup (GtkLabel *self,
   gtk_popover_popup (GTK_POPOVER (self->popup_menu));
 }
 
-static void
-gtk_label_popup_menu (GtkWidget  *widget,
-                      const char *action_name,
-                      GVariant   *parameters)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-
-  gtk_label_do_popup (self, -1, -1);
-}
-
-static void
-gtk_label_clear_links (GtkLabel *self)
-{
-  guint i;
-
-  if (!self->select_info)
-    return;
-
-  for (i = 0; i < self->select_info->n_links; i++)
-    link_free (&self->select_info->links[i]);
-  g_free (self->select_info->links);
-  self->select_info->links = NULL;
-  self->select_info->n_links = 0;
-  self->select_info->active_link = NULL;
-  gtk_widget_remove_css_class (GTK_WIDGET (self), "link");
-}
-
-static gboolean
-gtk_label_activate_link (GtkLabel    *self,
-                         const char *uri)
-{
-  GtkWidget *widget = GTK_WIDGET (self);
-  GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
-
-  if (!GTK_IS_WINDOW (toplevel))
-    return FALSE;
-
-  gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
-
-  return TRUE;
-}
-
-static void
-emit_activate_link (GtkLabel     *self,
-                    GtkLabelLink *link)
-{
-  gboolean handled;
-
-  g_signal_emit (self, signals[ACTIVATE_LINK], 0, link->uri, &handled);
-
-  /* signal handler might have invalidated the layout */
-  if (!self->layout)
-    return;
-
-  if (handled && !link->visited &&
-      self->select_info && self->select_info->links)
-    {
-      link->visited = TRUE;
-      update_link_state (self);
-    }
-}
-
-static void
-gtk_label_activate_current_link (GtkLabel *self)
-{
-  GtkLabelLink *link;
-  GtkWidget *widget = GTK_WIDGET (self);
-
-  link = gtk_label_get_focus_link (self, NULL);
-
-  if (link)
-    emit_activate_link (self, link);
-  else
-    gtk_widget_activate_default (widget);
-}
-
 /**
  * gtk_label_get_current_uri:
  * @self: a #GtkLabel
@@ -5596,52 +5496,6 @@ gtk_label_get_current_uri (GtkLabel *self)
   return NULL;
 }
 
-static gboolean
-gtk_label_query_tooltip (GtkWidget  *widget,
-                         int         x,
-                         int         y,
-                         gboolean    keyboard_tip,
-                         GtkTooltip *tooltip)
-{
-  GtkLabel *self = GTK_LABEL (widget);
-  GtkLabelSelectionInfo *info = self->select_info;
-  int index = -1;
-
-  if (info && info->links)
-    {
-      if (keyboard_tip)
-        {
-          if (info->selection_anchor == info->selection_end)
-            index = info->selection_anchor;
-        }
-      else
-        {
-          if (!get_layout_index (self, x, y, &index))
-            index = -1;
-        }
-
-      if (index != -1)
-        {
-          const int link_index = _gtk_label_get_link_at (self, index);
-
-          if (link_index != -1)
-            {
-              const GtkLabelLink *link = &info->links[link_index];
-
-              if (link->title)
-                {
-                  gtk_tooltip_set_markup (tooltip, link->title);
-                }
-            }
-        }
-    }
-
-  return GTK_WIDGET_CLASS (gtk_label_parent_class)->query_tooltip (widget,
-                                                                   x, y,
-                                                                   keyboard_tip,
-                                                                   tooltip);
-}
-
 int
 _gtk_label_get_cursor_position (GtkLabel *self)
 {
@@ -5862,7 +5716,7 @@ gtk_label_set_yalign (GtkLabel *self,
 {
   g_return_if_fail (GTK_IS_LABEL (self));
 
-  yalign = CLAMP (yalign, 0.0, 1.0); 
+  yalign = CLAMP (yalign, 0.0, 1.0);
 
   if (self->yalign == yalign)
     return;
diff --git a/gtk/gtklabelprivate.h b/gtk/gtklabelprivate.h
index 3533949e86..68ecc1a1e3 100644
--- a/gtk/gtklabelprivate.h
+++ b/gtk/gtklabelprivate.h
@@ -32,7 +32,7 @@ int _gtk_label_get_selection_bound (GtkLabel *label);
 int          _gtk_label_get_n_links     (GtkLabel *label);
 int          _gtk_label_get_link_at     (GtkLabel *label,
                                          int       pos);
-void         _gtk_label_activate_link   (GtkLabel *label, 
+void         _gtk_label_activate_link   (GtkLabel *label,
                                          int       idx);
 const char *_gtk_label_get_link_uri    (GtkLabel *label,
                                          int       idx);
@@ -44,7 +44,7 @@ gboolean     _gtk_label_get_link_visited (GtkLabel *label,
                                           int       idx);
 gboolean     _gtk_label_get_link_focused (GtkLabel *label,
                                           int       idx);
-                             
+
 G_END_DECLS
 
 #endif /* __GTK_LABEL_PRIVATE_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]