]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtklabel.c
Merge branch 'native-layout-incubator'
[~andy/gtk] / gtk / gtklabel.c
index 4577eec3b13fefb3bb65521e07404800f925fa4c..60ea406bc4fa26fbc1d656aaf3e9d429c6776291 100644 (file)
@@ -48,6 +48,7 @@
 #include "gtkimage.h"
 #include "gtkshow.h"
 #include "gtktooltip.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -58,6 +59,8 @@ typedef struct
   gint wrap_width;
   gint width_chars;
   gint max_width_chars;
+
+  gboolean mnemonics_visible;
 } GtkLabelPrivate;
 
 /* Notes about the handling of links:
@@ -151,6 +154,11 @@ enum {
   PROP_TRACK_VISITED_LINKS
 };
 
+/* When rotating ellipsizable text we want the natural size to request 
+ * more to ensure the label wont ever ellipsize in an allocation of full natural size.
+ * */
+#define ROTATION_ELLIPSIZE_PADDING 2
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
@@ -166,8 +174,6 @@ static void gtk_label_get_property      (GObject          *object,
                                         GParamSpec       *pspec);
 static void gtk_label_destroy           (GtkObject        *object);
 static void gtk_label_finalize          (GObject          *object);
-static void gtk_label_size_request      (GtkWidget        *widget,
-                                        GtkRequisition   *requisition);
 static void gtk_label_size_allocate     (GtkWidget        *widget,
                                          GtkAllocation    *allocation);
 static void gtk_label_state_changed     (GtkWidget        *widget,
@@ -216,7 +222,8 @@ static void gtk_label_set_attributes_internal    (GtkLabel      *label,
 static void gtk_label_set_uline_text_internal    (GtkLabel      *label,
                                                  const gchar   *str);
 static void gtk_label_set_pattern_internal       (GtkLabel      *label,
-                                                 const gchar   *pattern);
+                                                 const gchar   *pattern,
+                                                  gboolean       is_mnemonic);
 static void gtk_label_set_markup_internal        (GtkLabel      *label,
                                                  const gchar   *str,
                                                  gboolean       with_uline);
@@ -233,7 +240,7 @@ static void gtk_label_ensure_select_info  (GtkLabel *label);
 static void gtk_label_clear_select_info   (GtkLabel *label);
 static void gtk_label_update_cursor       (GtkLabel *label);
 static void gtk_label_clear_layout        (GtkLabel *label);
-static void gtk_label_ensure_layout       (GtkLabel *label);
+static void gtk_label_ensure_layout       (GtkLabel *label, gboolean guess_wrap_width);
 static void gtk_label_invalidate_wrap_width (GtkLabel *label);
 static void gtk_label_select_region_index (GtkLabel *label,
                                            gint      anchor_index,
@@ -264,6 +271,13 @@ static void     gtk_label_buildable_custom_finished    (GtkBuildable     *builda
                                                        gpointer          user_data);
 
 
+static void connect_mnemonics_visible_notify    (GtkLabel   *label);
+static gboolean      separate_uline_pattern     (const gchar  *str,
+                                                 guint        *accel_key,
+                                                 gchar       **new_str,
+                                                 gchar       **pattern);
+
+
 /* For selectable labels: */
 static void gtk_label_move_cursor        (GtkLabel        *label,
                                          GtkMovementStep  step,
@@ -291,13 +305,32 @@ static void          gtk_label_get_link_colors  (GtkWidget  *widget,
 static void          emit_activate_link         (GtkLabel     *label,
                                                  GtkLabelLink *link);
 
+static void     gtk_label_extended_layout_init  (GtkExtendedLayoutIface *iface);
+static gboolean gtk_label_is_height_for_width   (GtkExtendedLayout      *layout);
+static void     gtk_label_get_desired_width     (GtkExtendedLayout      *layout,
+                                                gint                   *minimum_size,
+                                                gint                   *natural_size);
+static void     gtk_label_get_desired_height    (GtkExtendedLayout      *layout,
+                                                gint                   *minimum_size,
+                                                gint                   *natural_size);
+static void     gtk_label_get_width_for_height  (GtkExtendedLayout      *layout,
+                                                gint                    height,
+                                                gint                   *minimum_width,
+                                                gint                   *natural_width);
+static void     gtk_label_get_height_for_width  (GtkExtendedLayout      *layout,
+                                                gint                    width,
+                                                gint                   *minimum_height,
+                                                gint                   *natural_height);
+
 static GQuark quark_angle = 0;
 
 static GtkBuildableIface *buildable_parent_iface = NULL;
 
 G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-                                               gtk_label_buildable_interface_init));
+                                               gtk_label_buildable_interface_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+                                                gtk_label_extended_layout_init));
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -338,7 +371,6 @@ gtk_label_class_init (GtkLabelClass *class)
 
   object_class->destroy = gtk_label_destroy;
 
-  widget_class->size_request = gtk_label_size_request;
   widget_class->size_allocate = gtk_label_size_allocate;
   widget_class->state_changed = gtk_label_state_changed;
   widget_class->style_set = gtk_label_style_set;
@@ -633,12 +665,13 @@ gtk_label_class_init (GtkLabelClass *class)
 
   /**
    * GtkLabel:width-chars:
-   * 
+   *
    * The desired width of the label, in characters. If this property is set to
-   * -1, the width will be calculated automatically, otherwise the label will
-   * request either 3 characters or the property value, whichever is greater.
-   * If the "width-chars" property is set to a positive value, then the 
-   * #GtkLabel:max-width-chars property is ignored. 
+   * -1, the width will be calculated automatically.
+   *
+   * See the section on <link linkend="label-text-layout">text layout</link>
+   * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
+   * determine the width of ellipsized and wrapped labels.
    *
    * Since: 2.6
    **/
@@ -695,11 +728,12 @@ gtk_label_class_init (GtkLabelClass *class)
    * 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, otherwise the 
-   * label will request space for no more than the requested number of 
-   * characters. If the #GtkLabel:width-chars property is set to a positive 
-   * value, then the "max-width-chars" property is ignored.
-   * 
+   * is set to -1, the width will be calculated automatically.
+   *
+   * See the section on <link linkend="label-text-layout">text layout</link>
+   * for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars
+   * determine the width of ellipsized and wrapped labels.
+   *
    * Since: 2.6
    **/
   g_object_class_install_property (gobject_class,
@@ -1011,7 +1045,7 @@ gtk_label_init (GtkLabel *label)
 {
   GtkLabelPrivate *priv;
 
-  GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
+  gtk_widget_set_has_window (GTK_WIDGET (label), FALSE);
 
   priv = GTK_LABEL_GET_PRIVATE (label);
   priv->width_chars = -1;
@@ -1037,6 +1071,8 @@ gtk_label_init (GtkLabel *label)
   label->mnemonic_widget = NULL;
   label->mnemonic_window = NULL;
 
+  priv->mnemonics_visible = TRUE;
+
   gtk_label_set_text (label, "");
 }
 
@@ -1119,7 +1155,6 @@ attribute_from_text (GtkBuilder   *builder,
                                              value, &val, error))
        attribute = pango_attr_gravity_hint_new (g_value_get_enum (&val));
       break;
-
       /* PangoAttrString */      
     case PANGO_ATTR_FAMILY:
       attribute = pango_attr_family_new (value);
@@ -1442,7 +1477,7 @@ gtk_label_mnemonic_activate (GtkWidget *widget,
   
   while (parent)
     {
-      if (GTK_WIDGET_CAN_FOCUS (parent) ||
+      if (gtk_widget_get_can_focus (parent) ||
          (!group_cycling && GTK_WIDGET_GET_CLASS (parent)->activate_signal) ||
           GTK_IS_NOTEBOOK (parent->parent) ||
          GTK_IS_MENU_ITEM (parent))
@@ -1486,10 +1521,12 @@ gtk_label_setup_mnemonic (GtkLabel *label,
     }
   
   if (label->mnemonic_keyval == GDK_VoidSymbol)
-    goto done;
+      goto done;
+
+  connect_mnemonics_visible_notify (GTK_LABEL (widget));
 
   toplevel = gtk_widget_get_toplevel (widget);
-  if (GTK_WIDGET_TOPLEVEL (toplevel))
+  if (gtk_widget_is_toplevel (toplevel))
     {
       GtkWidget *menu_shell;
       
@@ -1564,6 +1601,62 @@ label_shortcut_setting_changed (GtkSettings *settings)
   g_list_free (list);
 }
 
+static void
+mnemonics_visible_apply (GtkWidget *widget,
+                         gboolean   mnemonics_visible)
+{
+  GtkLabel *label;
+  GtkLabelPrivate *priv;
+
+  label = GTK_LABEL (widget);
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  mnemonics_visible = mnemonics_visible != FALSE;
+
+  if (priv->mnemonics_visible != mnemonics_visible)
+    {
+      priv->mnemonics_visible = mnemonics_visible;
+
+      gtk_label_recalculate (label);
+    }
+}
+
+static void
+label_mnemonics_visible_traverse_container (GtkWidget *widget,
+                                            gpointer   data)
+{
+  gboolean mnemonics_visible = GPOINTER_TO_INT (data);
+
+  _gtk_label_mnemonics_visible_apply_recursively (widget, mnemonics_visible);
+}
+
+void
+_gtk_label_mnemonics_visible_apply_recursively (GtkWidget *widget,
+                                                gboolean   mnemonics_visible)
+{
+  if (GTK_IS_LABEL (widget))
+    mnemonics_visible_apply (widget, mnemonics_visible);
+  else if (GTK_IS_CONTAINER (widget))
+    gtk_container_forall (GTK_CONTAINER (widget),
+                          label_mnemonics_visible_traverse_container,
+                          GINT_TO_POINTER (mnemonics_visible));
+}
+
+static void
+label_mnemonics_visible_changed (GtkWindow  *window,
+                                 GParamSpec *pspec,
+                                 gpointer    data)
+{
+  gboolean mnemonics_visible;
+
+  g_object_get (window, "mnemonics-visible", &mnemonics_visible, NULL);
+
+  gtk_container_forall (GTK_CONTAINER (window),
+                        label_mnemonics_visible_traverse_container,
+                        GINT_TO_POINTER (mnemonics_visible));
+}
+
 static void
 gtk_label_screen_changed (GtkWidget *widget,
                          GdkScreen *old_screen)
@@ -1610,7 +1703,7 @@ label_mnemonic_widget_weak_notify (gpointer      data,
 /**
  * gtk_label_set_mnemonic_widget:
  * @label: a #GtkLabel
- * @widget: the target #GtkWidget 
+ * @widget: (allow-none): the target #GtkWidget
  *
  * If the label has been set so that it has an mnemonic key (using
  * i.e. gtk_label_set_markup_with_mnemonic(),
@@ -1750,20 +1843,23 @@ gtk_label_compose_effective_attrs (GtkLabel *label)
       if (label->effective_attrs)
        {
          if ((iter = pango_attr_list_get_iterator (label->attrs)))
-           do 
-             {
-               iter_attrs = pango_attr_iterator_get_attrs (iter);
-               for (l = iter_attrs; l; l = l->next)
-                 {
-                   attr = l->data;
-                   pango_attr_list_insert (label->effective_attrs, attr);
-                 }
-               g_slist_free (iter_attrs);
-             }
-           while (pango_attr_iterator_next (iter));
+           {
+             do
+               {
+                 iter_attrs = pango_attr_iterator_get_attrs (iter);
+                 for (l = iter_attrs; l; l = l->next)
+                   {
+                     attr = l->data;
+                     pango_attr_list_insert (label->effective_attrs, attr);
+                   }
+                 g_slist_free (iter_attrs);
+               }
+             while (pango_attr_iterator_next (iter));
+             pango_attr_iterator_destroy (iter);
+           }
        }
       else
-       label->effective_attrs = 
+       label->effective_attrs =
          pango_attr_list_ref (label->attrs);
     }
 }
@@ -1792,28 +1888,22 @@ gtk_label_recalculate (GtkLabel *label)
   guint keyval = label->mnemonic_keyval;
 
   if (label->use_markup)
-    {
-      gtk_label_set_markup_internal (label, label->label, label->use_underline);
-      gtk_label_compose_effective_attrs (label);
-    }
+    gtk_label_set_markup_internal (label, label->label, label->use_underline);
   else
     {
       if (label->use_underline)
-       {
-         gtk_label_set_uline_text_internal (label, label->label);
-         gtk_label_compose_effective_attrs (label);
-       }
+       gtk_label_set_uline_text_internal (label, label->label);
       else
-       {
-         gtk_label_set_text_internal (label, g_strdup (label->label));
-         if (label->attrs)
-           pango_attr_list_ref (label->attrs);
-         if (label->effective_attrs)
-           pango_attr_list_unref (label->effective_attrs);
-         label->effective_attrs = label->attrs;
-       }
+        {
+          if (label->effective_attrs)
+            pango_attr_list_unref (label->effective_attrs);
+          label->effective_attrs = NULL;
+          gtk_label_set_text_internal (label, g_strdup (label->label));
+        }
     }
 
+  gtk_label_compose_effective_attrs (label);
+
   if (!label->use_underline)
     label->mnemonic_keyval = GDK_VoidSymbol;
 
@@ -1880,7 +1970,7 @@ gtk_label_set_attributes (GtkLabel         *label,
 
   gtk_label_recalculate (label);
 
-  gtk_label_clear_layout (label);  
+  gtk_label_clear_layout (label);
   gtk_widget_queue_resize (GTK_WIDGET (label));
 }
 
@@ -2236,6 +2326,7 @@ gtk_label_set_markup_internal (GtkLabel    *label,
                                const gchar *str,
                                gboolean     with_uline)
 {
+  GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
   gchar *text = NULL;
   GError *error = NULL;
   PangoAttrList *attrs = NULL;
@@ -2259,6 +2350,35 @@ gtk_label_set_markup_internal (GtkLabel    *label,
       gtk_label_ensure_has_tooltip (label);
     }
 
+  if (with_uline)
+    {
+      gboolean enable_mnemonics;
+      gboolean auto_mnemonics;
+
+      g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
+                    "gtk-enable-mnemonics", &enable_mnemonics,
+                    "gtk-auto-mnemonics", &auto_mnemonics,
+                    NULL);
+
+      if (!(enable_mnemonics && priv->mnemonics_visible &&
+            (!auto_mnemonics ||
+             (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
+              (!label->mnemonic_widget ||
+               gtk_widget_is_sensitive (label->mnemonic_widget))))))
+        {
+          gchar *tmp;
+          gchar *pattern;
+          guint key;
+
+          if (separate_uline_pattern (new_str, &key, &tmp, &pattern))
+            {
+              g_free (new_str);
+              new_str = tmp;
+              g_free (pattern);
+            }
+        }
+    }
+
   if (!pango_parse_markup (new_str,
                            -1,
                            with_uline ? '_' : 0,
@@ -2418,24 +2538,37 @@ gtk_label_pattern_to_attrs (GtkLabel      *label,
 
 static void
 gtk_label_set_pattern_internal (GtkLabel    *label,
-                               const gchar *pattern)
+                               const gchar *pattern,
+                                gboolean     is_mnemonic)
 {
+  GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
   PangoAttrList *attrs;
   gboolean enable_mnemonics;
+  gboolean auto_mnemonics;
 
   g_return_if_fail (GTK_IS_LABEL (label));
 
   if (label->pattern_set)
     return;
 
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
-               "gtk-enable-mnemonics", &enable_mnemonics,
-               NULL);
+  if (is_mnemonic)
+    {
+      g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
+                   "gtk-enable-mnemonics", &enable_mnemonics,
+                   "gtk-auto-mnemonics", &auto_mnemonics,
+                   NULL);
 
-  if (enable_mnemonics && pattern)
-    attrs = gtk_label_pattern_to_attrs (label, pattern);
+      if (enable_mnemonics && priv->mnemonics_visible && pattern &&
+          (!auto_mnemonics ||
+           (gtk_widget_is_sensitive (GTK_WIDGET (label)) &&
+            (!label->mnemonic_widget ||
+             gtk_widget_is_sensitive (label->mnemonic_widget)))))
+        attrs = gtk_label_pattern_to_attrs (label, pattern);
+      else
+        attrs = NULL;
+    }
   else
-    attrs = NULL;
+    attrs = gtk_label_pattern_to_attrs (label, pattern);
 
   if (label->effective_attrs)
     pango_attr_list_unref (label->effective_attrs);
@@ -2452,13 +2585,13 @@ gtk_label_set_pattern (GtkLabel    *label,
 
   if (pattern)
     {
-      gtk_label_set_pattern_internal (label, pattern);
+      gtk_label_set_pattern_internal (label, pattern, FALSE);
       label->pattern_set = TRUE;
     }
   else
     gtk_label_recalculate (label);
 
-  gtk_label_clear_layout (label);  
+  gtk_label_clear_layout (label);
   gtk_widget_queue_resize (GTK_WIDGET (label));
 }
 
@@ -2802,17 +2935,25 @@ gtk_label_clear_layout (GtkLabel *label)
     }
 }
 
-static gint
-get_label_char_width (GtkLabel *label)
+
+static void
+get_label_width (GtkLabel *label,
+                gint     *minimum,
+                gint     *natural)
 {
-  GtkLabelPrivate *priv;
-  PangoContext *context;
+  GtkWidgetAuxInfo *aux_info;
+  GtkLabelPrivate  *priv;
+  PangoLayout      *layout;
+  PangoContext     *context;
   PangoFontMetrics *metrics;
-  gint char_width, digit_width, char_pixels, w;
-  
-  priv = GTK_LABEL_GET_PRIVATE (label);
-  
-  context = pango_layout_get_context (label->layout);
+  PangoRectangle    rect;
+  gint              char_width, digit_width, char_pixels, text_width, ellipsize_chars, guess_width;
+
+  priv     = GTK_LABEL_GET_PRIVATE (label);
+  aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
+
+  layout  = pango_layout_copy (label->layout);
+  context = pango_layout_get_context (layout);
   metrics = pango_context_get_metrics (context, GTK_WIDGET (label)->style->font_desc, 
                                       pango_context_get_language (context));
   
@@ -2820,24 +2961,88 @@ get_label_char_width (GtkLabel *label)
   digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
   char_pixels = MAX (char_width, digit_width);
   pango_font_metrics_unref (metrics);
-  
-  if (priv->width_chars < 0)
-    {
-      PangoRectangle rect;
-      
-      pango_layout_set_width (label->layout, -1);
-      pango_layout_get_extents (label->layout, NULL, &rect);
       
-      w = char_pixels * MAX (priv->max_width_chars, 3);
-      w = MIN (rect.width, w);
+  /* Fetch the length of the complete unwrapped text */
+  pango_layout_set_width (layout, -1);
+  pango_layout_get_extents (layout, NULL, &rect);
+  text_width = rect.width;
+
+  /* Fetch the width that was guessed by gtk_label_ensure_layout() */
+  pango_layout_get_extents (label->layout, NULL, &rect);
+  guess_width = rect.width;
+
+  /* enforce minimum width for ellipsized labels at ~3 chars */
+  if (label->ellipsize)
+    ellipsize_chars = 3;
+  else
+    ellipsize_chars = 0;
+
+  /* "width-chars" Hard-coded minimum width: 
+   *    - minimum size should be MAX (width-chars, strlen ("..."));
+   *    - natural size should be MAX (width-chars, strlen (label->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 (label->text))
+   *
+   *    For ellipsizing labels; if max-width-chars is specified: either it is used as 
+   *    a minimum size or the label text as a minimum size (natural size still overflows).
+   *
+   *    For wrapping labels; A reasonable minimum size is useful to naturally layout
+   *    interfaces automatically. In this case if no "width-chars" is specified, the minimum
+   *    width will default to the wrap guess that gtk_label_ensure_layout() does.
+   *
+   *    In *any* case the minimum width is completely overridden if an explicit width 
+   *    request was provided.
+   */
+
+  if (label->ellipsize || label->wrap)
+    {
+      *minimum = char_pixels * MAX (priv->width_chars, ellipsize_chars);
+
+      /* Default to the minimum width regularly guessed by GTK+ if no minimum
+       * width was specified, only allow unwrapping of these labels.
+       *
+       * Note that when specifying a small width_chars for a long text;
+       * an accordingly large size will be required for the label height.
+       */
+      if (label->wrap && priv->width_chars <= 0)
+       *minimum = guess_width;
+
+      if (priv->max_width_chars < 0)
+       {
+         *natural = MAX (*minimum, text_width);
+       }
+      else
+       {
+         gint max_char_width = char_pixels * priv->max_width_chars;
+         gint max_width      = MIN (text_width, max_char_width);
+
+         /* With max-char-width specified, we let the minimum widths of 
+          * ellipsized text crawl up to the max-char-width
+          * (note that we dont want to limit the minimum width for wrapping text).
+          */
+         if (label->ellipsize)
+           *minimum = MIN (text_width, max_width);
+
+         *natural = MAX (*minimum, max_width);
+       }
     }
   else
     {
-      /* enforce minimum width for ellipsized labels at ~3 chars */
-      w = char_pixels * MAX (priv->width_chars, 3);
+      *minimum = text_width;
+      *natural = *minimum;
     }
-  
-  return w;
+
+  /* if a width-request is set, use that as the requested label width */
+  if ((label->wrap || label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0) &&
+      aux_info && aux_info->width > 0)
+    {
+      *minimum = aux_info->width * PANGO_SCALE;
+      *natural = MAX (*natural, *minimum);
+    }
+
+  g_object_unref (layout);
 }
 
 static void
@@ -2859,14 +3064,38 @@ get_label_wrap_width (GtkLabel *label)
   
   if (priv->wrap_width < 0)
     {
-      if (priv->width_chars > 0 || priv->max_width_chars > 0)
-       priv->wrap_width = get_label_char_width (label);
+      if (priv->width_chars > 0)
+       {
+         PangoLayout      *layout;
+         PangoContext     *context;
+         PangoFontMetrics *metrics;
+         PangoRectangle    rect;
+         gint              char_width, digit_width, char_pixels, text_width;
+
+         layout  = pango_layout_copy (label->layout);
+         context = pango_layout_get_context (layout);
+         metrics = pango_context_get_metrics (context, GTK_WIDGET (label)->style->font_desc, 
+                                              pango_context_get_language (context));
+         
+         char_width = pango_font_metrics_get_approximate_char_width (metrics);
+         digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
+         char_pixels = MAX (char_width, digit_width);
+         pango_font_metrics_unref (metrics);
+         
+         pango_layout_set_width (layout, -1);
+         pango_layout_get_extents (layout, NULL, &rect);
+         g_object_unref (layout);
+
+         text_width = rect.width;
+
+         priv->wrap_width = PANGO_PIXELS (MAX (text_width, char_pixels * priv->width_chars));
+       }
       else
        {
          PangoLayout *layout;
   
          layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), 
-                                                  "This long string gives a good enough length for any line to have.");
+                                                  "This string is just about long enough.");
          pango_layout_get_size (layout, &priv->wrap_width, NULL);
          g_object_unref (layout);
        }
@@ -2876,7 +3105,7 @@ get_label_wrap_width (GtkLabel *label)
 }
 
 static void
-gtk_label_ensure_layout (GtkLabel *label)
+gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
 {
   GtkWidget *widget;
   PangoRectangle logical_rect;
@@ -2891,14 +3120,14 @@ gtk_label_ensure_layout (GtkLabel *label)
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
       gdouble angle = gtk_label_get_angle (label);
 
-      if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
+      if (angle != 0.0 && !label->select_info)
        {
+          PangoMatrix matrix = PANGO_MATRIX_INIT;
+
          /* We rotate the standard singleton PangoContext for the widget,
           * depending on the fact that it's meant pretty much exclusively
           * for our use.
           */
-         PangoMatrix matrix = PANGO_MATRIX_INIT;
-         
          pango_matrix_rotate (&matrix, angle);
 
          pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
@@ -2944,19 +3173,33 @@ gtk_label_ensure_layout (GtkLabel *label)
       pango_layout_set_single_paragraph_mode (label->layout, label->single_line_mode);
 
       if (label->ellipsize)
-       pango_layout_set_width (label->layout, 
-                               widget->allocation.width * PANGO_SCALE);
+        pango_layout_set_width (label->layout,
+                                widget->allocation.width * PANGO_SCALE);
       else if (label->wrap)
        {
-         GtkWidgetAuxInfo *aux_info;
+         GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
          gint longest_paragraph;
          gint width, height;
-
-         pango_layout_set_wrap (label->layout, label->wrap_mode);
-         
-         aux_info = _gtk_widget_get_aux_info (widget, FALSE);
-         if (aux_info && aux_info->width > 0)
-           pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
+         gint aux_width = 0;
+
+         if ((angle == 90 || angle == 270) && aux_info && aux_info->height > 0)
+           aux_width = aux_info->height;
+         else if (aux_info && aux_info->width > 0)
+           aux_width = aux_info->width;
+
+         if (aux_width > 0)
+           pango_layout_set_width (label->layout, aux_width * PANGO_SCALE);
+         else if (guess_wrap_width == FALSE &&
+                  widget->allocation.width > 1 && widget->allocation.height > 1)
+           {
+             if (angle == 90 || angle == 270)
+               width = widget->allocation.height - label->misc.ypad * 2;
+             else
+               width = widget->allocation.width  - label->misc.xpad * 2;
+
+             pango_layout_set_wrap (label->layout, label->wrap_mode);
+             pango_layout_set_width (label->layout, MAX (width, 1) * PANGO_SCALE);
+           }
          else
            {
              GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (label));
@@ -2966,7 +3209,6 @@ gtk_label_ensure_layout (GtkLabel *label)
              pango_layout_get_extents (label->layout, NULL, &logical_rect);
 
              width = logical_rect.width;
-             
              /* Try to guess a reasonable maximum width */
              longest_paragraph = width;
 
@@ -2974,12 +3216,12 @@ gtk_label_ensure_layout (GtkLabel *label)
              width = MIN (width, wrap_width);
              width = MIN (width,
                           PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
-             
+
              pango_layout_set_width (label->layout, width);
              pango_layout_get_extents (label->layout, NULL, &logical_rect);
              width = logical_rect.width;
              height = logical_rect.height;
-             
+
              /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
               * so we try short search for a narrower width that leaves us with the same height
               */
@@ -3020,91 +3262,309 @@ gtk_label_ensure_layout (GtkLabel *label)
     }
 }
 
+static gint
+get_single_line_height (GtkWidget   *widget,
+                        PangoLayout *layout)
+{
+  PangoContext *context;
+  PangoFontMetrics *metrics;
+  gint ascent, descent;
+
+  context = pango_layout_get_context (layout);
+  metrics = pango_context_get_metrics (context, widget->style->font_desc,
+                                       pango_context_get_language (context));
+
+  ascent = pango_font_metrics_get_ascent (metrics);
+  descent = pango_font_metrics_get_descent (metrics);
+  pango_font_metrics_unref (metrics);
+
+  return ascent + descent;
+}
+
 static void
-gtk_label_size_request (GtkWidget      *widget,
-                       GtkRequisition *requisition)
+gtk_label_extended_layout_init (GtkExtendedLayoutIface *iface)
 {
-  GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPrivate *priv;
-  gint width, height;
-  PangoRectangle logical_rect;
-  GtkWidgetAuxInfo *aux_info;
+  iface->is_height_for_width  = gtk_label_is_height_for_width;
+  iface->get_desired_width    = gtk_label_get_desired_width;
+  iface->get_desired_height   = gtk_label_get_desired_height;
+  iface->get_width_for_height = gtk_label_get_width_for_height;
+  iface->get_height_for_width = gtk_label_get_height_for_width;
+}
+
+static gboolean
+gtk_label_is_height_for_width (GtkExtendedLayout *layout)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  gdouble   angle = gtk_label_get_angle (label);
+
+  if (angle == 90 || angle == 270)
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+get_size_for_allocation (GtkLabel        *label,
+                         GtkOrientation   orientation,
+                         gint             allocation,
+                         gint            *minimum_size,
+                         gint            *natural_size)
+{
+  PangoLayout *layout;
+  GtkWidgetAuxInfo *aux_info =
+    _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
+  gint aux_size;
+  gint text_height;
+
+  gtk_label_ensure_layout (label, FALSE);
+  layout = pango_layout_copy (label->layout);
+
+  if (aux_info)
+    {
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        aux_size = aux_info->width;
+      else
+        aux_size = aux_info->height;
+    }
+  else
+    aux_size = 0;
+
+  if (aux_size > 0)
+    pango_layout_set_width (layout, aux_size * PANGO_SCALE);
+  else
+    pango_layout_set_width (layout, allocation * PANGO_SCALE);
+
+  pango_layout_get_pixel_size (layout, NULL, &text_height);
 
-  priv = GTK_LABEL_GET_PRIVATE (widget);
+  if (minimum_size)
+    *minimum_size = text_height;
 
-  /*  
-   * If word wrapping is on, then the height requisition can depend
-   * on:
+  if (natural_size)
+    *natural_size = text_height;
+
+  g_object_unref (layout);
+}
+
+static void
+gtk_label_get_desired_size (GtkExtendedLayout *layout,
+                            GtkOrientation     orientation,
+                            gint              *minimum_size,
+                            gint              *natural_size)
+{
+  GtkLabel      *label = GTK_LABEL (layout);
+  PangoRectangle required_rect;
+  PangoRectangle natural_rect;
+  gdouble        angle;
+
+  /* "width-chars" Hard-coded minimum width:
+   *    - minimum size should be MAX (width-chars, strlen ("..."));
+   *    - natural size should be MAX (width-chars, strlen (label->text));
    *
-   *   - Any width set on the widget via gtk_widget_set_size_request().
-   *   - The padding of the widget (xpad, set by gtk_misc_set_padding)
+   * "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 (label->text))
    *
-   * Instead of trying to detect changes to these quantities, if we
-   * are wrapping, we just rewrap for each size request. Since
-   * size requisitions are cached by the GTK+ core, this is not
-   * expensive.
    */
 
+  /* When calculating ->wrap sometimes we need to invent a size; Ideally we should be doing
+   * that stuff here instead of inside gtk_label_ensure_layout() */
   if (label->wrap)
     gtk_label_clear_layout (label);
+  gtk_label_ensure_layout (label, TRUE);
+
+  angle = gtk_label_get_angle (label);
+
+  /* Start off with the pixel extents of the rendered layout */
+  pango_layout_get_extents (label->layout, NULL, &required_rect);
+  required_rect.x = required_rect.y = 0;
 
-  gtk_label_ensure_layout (label);
+  if (label->single_line_mode || label->wrap)
+    required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
 
-  width = label->misc.xpad * 2;
-  height = label->misc.ypad * 2;
+  natural_rect = required_rect;
 
-  aux_info = _gtk_widget_get_aux_info (widget, FALSE);
+  /* Calculate text width itself based on GtkLabel property rules */
+  get_label_width (label, &required_rect.width, &natural_rect.width);
 
+  /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
   if (label->have_transform)
     {
-      PangoRectangle rect;
-      PangoContext *context = pango_layout_get_context (label->layout);
-      const PangoMatrix *matrix = pango_context_get_matrix (context);
+      PangoLayout       *layout  = pango_layout_copy (label->layout);
+      PangoContext      *context = pango_layout_get_context (label->layout);
+      const PangoMatrix *matrix  = pango_context_get_matrix (context);
 
-      pango_layout_get_extents (label->layout, NULL, &rect);
-      pango_matrix_transform_rectangle (matrix, &rect);
-      pango_extents_to_pixels (&rect, NULL);
-      
-      requisition->width = width + rect.width;
-      requisition->height = height + rect.height;
+      pango_layout_set_width (layout, -1);
+      pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
 
-      return;
+      pango_layout_get_extents (layout, NULL, &natural_rect);
+      g_object_unref (layout);
+
+      pango_matrix_transform_rectangle (matrix, &required_rect);
+      pango_matrix_transform_rectangle (matrix, &natural_rect);
+
+      /* Bump the natural size in case of ellipsize to ensure pango has
+       * enough space in the angles (note, we could alternatively set the
+       * layout to not ellipsize when we know we have been allocated our
+       * full natural size, or it may be that pango needs a fix here).
+       */
+      if (label->ellipsize && angle != 0 && angle != 90 && angle != 180 && angle != 270 && angle != 360)
+        {
+          /* For some reason we only need this at about 110 degrees, and only
+           * when gaining in height
+           */
+          natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          natural_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+        }
     }
-  else
-    pango_layout_get_extents (label->layout, NULL, &logical_rect);
 
-  if ((label->wrap || label->ellipsize || 
-       priv->width_chars > 0 || priv->max_width_chars > 0) && 
-      aux_info && aux_info->width > 0)
-    width += aux_info->width;
-  else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
+  required_rect.width  = PANGO_PIXELS_CEIL (required_rect.width);
+  required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+
+  natural_rect.width  = PANGO_PIXELS_CEIL (natural_rect.width);
+  natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
     {
-      width += PANGO_PIXELS (get_label_char_width (label));
+      /* Note, we cant use get_size_for_allocation() when rotating
+       * ellipsized labels.
+       */
+      if (!(label->ellipsize && label->have_transform) &&
+          (angle == 90 || angle == 270))
+        {
+          /* Doing a h4w request on a rotated label here, return the
+           * required width for the minimum height.
+           */
+          get_size_for_allocation (label,
+                                   GTK_ORIENTATION_VERTICAL,
+                                   required_rect.height,
+                                   minimum_size, natural_size);
+
+        }
+      else
+        {
+          /* Normal desired width */
+          *minimum_size = required_rect.width;
+          *natural_size = natural_rect.width;
+        }
+
+      *minimum_size += label->misc.xpad * 2;
+      *natural_size += label->misc.xpad * 2;
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Note, we cant use get_size_for_allocation() when rotating
+       * ellipsized labels.
+       */
+      if (!(label->ellipsize && label->have_transform) &&
+          (angle == 0 || angle == 180))
+        {
+          /* Doing a w4h request on a label here, return the required
+           * height for the minimum width.
+           */
+          get_size_for_allocation (label,
+                                   GTK_ORIENTATION_HORIZONTAL,
+                                   required_rect.width,
+                                   minimum_size, natural_size);
+        }
+      else
+        {
+          /* A vertically rotated label does w4h, so return the base
+           * desired height (text length)
+           */
+          *minimum_size = required_rect.height;
+          *natural_size = natural_rect.height;
+        }
+
+      *minimum_size += label->misc.ypad * 2;
+      *natural_size += label->misc.ypad * 2;
     }
-  else
-    width += PANGO_PIXELS (logical_rect.width);
 
-  if (label->single_line_mode)
+  /* Restore real allocated size of layout; sometimes size-requests
+   * are randomly called without a following allocation; for this case
+   * we need to make sure we dont have a mucked up layout because we
+   * went and guessed the wrap-size.
+   */
+  if (label->wrap)
+    gtk_label_clear_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
+
+}
+
+
+static void
+gtk_label_get_desired_width (GtkExtendedLayout *layout,
+                             gint              *minimum_size,
+                             gint              *natural_size)
+{
+  gtk_label_get_desired_size (layout,
+                              GTK_ORIENTATION_HORIZONTAL,
+                              minimum_size, natural_size);
+}
+
+static void
+gtk_label_get_desired_height (GtkExtendedLayout *layout,
+                              gint              *minimum_size,
+                              gint              *natural_size)
+{
+  gtk_label_get_desired_size (layout,
+                              GTK_ORIENTATION_VERTICAL,
+                              minimum_size, natural_size);
+}
+
+static void
+gtk_label_get_width_for_height (GtkExtendedLayout *layout,
+                                gint               height,
+                                gint              *minimum_width,
+                                gint              *natural_width)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  gdouble angle = gtk_label_get_angle (label);
+
+  if (label->wrap && (angle == 90 || angle == 270))
     {
-      PangoContext *context;
-      PangoFontMetrics *metrics;
-      gint ascent, descent;
+      if (label->wrap)
+        gtk_label_clear_layout (label);
 
-      context = pango_layout_get_context (label->layout);
-      metrics = pango_context_get_metrics (context, widget->style->font_desc,
-                                           pango_context_get_language (context));
+      get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
+                               MAX (1, height - (label->misc.ypad * 2)),
+                               minimum_width, natural_width);
 
-      ascent = pango_font_metrics_get_ascent (metrics);
-      descent = pango_font_metrics_get_descent (metrics);
-      pango_font_metrics_unref (metrics);
-    
-      height += PANGO_PIXELS (ascent + descent);
+      if (minimum_width)
+        *minimum_width += label->misc.xpad * 2;
+
+      if (natural_width)
+        *natural_width += label->misc.xpad * 2;
     }
   else
-    height += PANGO_PIXELS (logical_rect.height);
+    GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_desired_width (layout, minimum_width, natural_width);
+}
+
+static void
+gtk_label_get_height_for_width (GtkExtendedLayout *layout,
+                                gint               width,
+                                gint              *minimum_height,
+                                gint              *natural_height)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  gdouble angle = gtk_label_get_angle (label);
+
+  if (label->wrap && (angle == 0 || angle == 180 || angle == 360))
+    {
+      if (label->wrap)
+        gtk_label_clear_layout (label);
+
+      get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
+                               MAX (1, width - label->misc.xpad * 2),
+                               minimum_height, natural_height);
 
-  requisition->width = width;
-  requisition->height = height;
+      if (minimum_height)
+        *minimum_height += label->misc.ypad * 2;
+
+      if (natural_height)
+        *natural_height += label->misc.ypad * 2;
+    }
+  else
+    GTK_EXTENDED_LAYOUT_GET_IFACE (layout)->get_desired_height (layout, minimum_height, natural_height);
 }
 
 static void
@@ -3117,21 +3577,86 @@ gtk_label_size_allocate (GtkWidget     *widget,
 
   GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate (widget, allocation);
 
+  /* The layout may have been recently cleared in get_size_for_orientation(),
+   * but the width at that point may not be the same as the allocated width
+   */
+  if (label->wrap)
+    gtk_label_clear_layout (label);
+
+  gtk_label_ensure_layout (label, FALSE);
+
   if (label->ellipsize)
     {
       if (label->layout)
-       {
-         gint width;
-         PangoRectangle logical;
+        {
+          PangoRectangle logical;
+          PangoRectangle bounds;
 
-         width = (allocation->width - label->misc.xpad * 2) * PANGO_SCALE;
+          bounds.x = bounds.y = 0;
+          bounds.width = allocation->width - label->misc.xpad * 2;
+          bounds.height = allocation->height - label->misc.ypad * 2;
 
-         pango_layout_set_width (label->layout, -1);
-         pango_layout_get_extents (label->layout, NULL, &logical);
+          pango_layout_set_width (label->layout, -1);
+          pango_layout_get_pixel_extents (label->layout, NULL, &logical);
 
-         if (logical.width > width)
-           pango_layout_set_width (label->layout, width);
-       }
+          if (label->have_transform)
+            {
+              PangoContext *context = gtk_widget_get_pango_context (widget);
+              const PangoMatrix *matrix = pango_context_get_matrix (context);
+
+              const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
+              const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
+              if (fabs (dy) < 0.01)
+                {
+                  if (logical.width > bounds.width)
+                    pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
+                }
+              else if (fabs (dx) < 0.01)
+                {
+                  if (logical.width > bounds.height)
+                    pango_layout_set_width (label->layout, bounds.height * PANGO_SCALE);
+                }
+              else
+                {
+                  gdouble x0, y0, x1, y1, length;
+                  gboolean vertical;
+                  gint cy;
+
+                  x0 = bounds.width / 2;
+                  y0 = dx ? x0 * dy / dx : dy * INFINITY;
+                  vertical = fabs (y0) > bounds.height / 2;
+
+                  if (vertical)
+                    {
+                      y0 = bounds.height/2;
+                      x0 = dy ? y0 * dx / dy : dx * INFINITY;
+                    }
+
+                  length = 2 * sqrt (x0 * x0 + y0 * y0);
+                  pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
+                  pango_layout_get_pixel_size (label->layout, NULL, &cy);
+
+                  x1 = +dy * cy/2;
+                  y1 = -dx * cy/2;
+
+                  if (vertical)
+                    {
+                      y0 = bounds.height/2 + y1 - y0;
+                      x0 = -y0 * dx/dy;
+                    }
+                  else
+                    {
+                      x0 = bounds.width/2 + x1 - x0;
+                      y0 = -x0 * dy/dx;
+                    }
+                  length = length - sqrt (x0 * x0 + y0 * y0) * 2;
+                  pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
+                }
+            }
+          else if (logical.width > bounds.width)
+            pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
+        }
     }
 
   if (label->select_info && label->select_info->window)
@@ -3147,17 +3672,21 @@ gtk_label_size_allocate (GtkWidget     *widget,
 static void
 gtk_label_update_cursor (GtkLabel *label)
 {
+  GtkWidget *widget;
+
   if (!label->select_info)
     return;
 
-  if (GTK_WIDGET_REALIZED (label))
+  widget = GTK_WIDGET (label);
+
+  if (gtk_widget_get_realized (widget))
     {
       GdkDisplay *display;
       GdkCursor *cursor;
 
-      if (GTK_WIDGET_IS_SENSITIVE (label))
+      if (gtk_widget_is_sensitive (widget))
         {
-          display = gtk_widget_get_display (GTK_WIDGET (label));
+          display = gtk_widget_get_display (widget);
 
           if (label->select_info->active_link)
             cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
@@ -3221,36 +3750,52 @@ get_layout_location (GtkLabel  *label,
                      gint      *yp)
 {
   GtkMisc *misc;
-  GtkWidget *widget; 
+  GtkWidget *widget;
   GtkLabelPrivate *priv;
   gfloat xalign;
   gint req_width, x, y;
+  gint req_height;
   PangoRectangle logical;
-  
-  misc = GTK_MISC (label);
+  gdouble angle;
+
+  misc   = GTK_MISC (label);
   widget = GTK_WIDGET (label);
-  priv = GTK_LABEL_GET_PRIVATE (label);
+  priv   = GTK_LABEL_GET_PRIVATE (label);
+  angle  = gtk_label_get_angle (label);
 
   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
     xalign = misc->xalign;
   else
     xalign = 1.0 - misc->xalign;
 
-  pango_layout_get_pixel_extents (label->layout, NULL, &logical);
+  pango_layout_get_extents (label->layout, NULL, &logical);
 
-  if (label->ellipsize || priv->width_chars > 0)
+  /* Do the wrap width delimiting before the transform
+   */
+  if (label->wrap || label->ellipsize || priv->width_chars > 0)
     {
       int width;
 
       width = pango_layout_get_width (label->layout);
 
-      req_width = logical.width;
       if (width != -1)
-       req_width = MIN(PANGO_PIXELS (width), req_width);
-      req_width += 2 * misc->xpad;
+       logical.width = MIN (width, logical.width);
     }
-  else
-    req_width = widget->requisition.width;
+
+  if (label->have_transform)
+    {
+      PangoContext *context = gtk_widget_get_pango_context (widget);
+      const PangoMatrix *matrix = pango_context_get_matrix (context);
+      pango_matrix_transform_rectangle (matrix, &logical);
+    }
+
+  pango_extents_to_pixels (&logical, NULL);
+
+  req_width  = logical.width;
+  req_height = logical.height;
+
+  req_width  += 2 * misc->xpad;
+  req_height += 2 * misc->ypad;
 
   x = floor (widget->allocation.x + (gint)misc->xpad +
              xalign * (widget->allocation.width - req_width));
@@ -3259,7 +3804,9 @@ get_layout_location (GtkLabel  *label,
     x = MAX (x, widget->allocation.x + misc->xpad);
   else
     x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
-  x -= logical.x;
+
+
+
 
   /* bgo#315462 - For single-line labels, *do* align the requisition with
    * respect to the allocation, even if we are under-allocated.  For multi-line
@@ -3276,10 +3823,10 @@ get_layout_location (GtkLabel  *label,
    */
   if (pango_layout_get_line_count (label->layout) == 1)
     y = floor (widget->allocation.y + (gint)misc->ypad 
-              + (widget->allocation.height - widget->requisition.height) * misc->yalign);
+              + (widget->allocation.height - req_height) * misc->yalign);
   else
     y = floor (widget->allocation.y + (gint)misc->ypad 
-              + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
+              + MAX (((widget->allocation.height - req_height) * misc->yalign),
                      0));
 
   if (xp)
@@ -3316,7 +3863,7 @@ get_cursor_direction (GtkLabel *label)
 
   g_assert (label->select_info);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
 
   for (l = pango_layout_get_lines_readonly (label->layout); l; l = l->next)
     {
@@ -3339,13 +3886,15 @@ get_cursor_direction (GtkLabel *label)
 static void
 gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
 {
+  GtkWidget *widget;
+
   if (label->select_info == NULL)
     return;
+
+  widget = GTK_WIDGET (label);
   
-  if (GTK_WIDGET_DRAWABLE (label))
+  if (gtk_widget_is_drawable (widget))
     {
-      GtkWidget *widget = GTK_WIDGET (label);
-
       PangoDirection keymap_direction;
       PangoDirection cursor_direction;
       PangoRectangle strong_pos, weak_pos;
@@ -3359,7 +3908,7 @@ gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
       cursor_direction = get_cursor_direction (label);
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
                                   &strong_pos, &weak_pos);
@@ -3443,16 +3992,16 @@ gtk_label_expose (GtkWidget      *widget,
   GtkLabelSelectionInfo *info = label->select_info;
   gint x, y;
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
   
-  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
+  if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
       label->text && (*label->text != '\0'))
     {
       get_layout_location (label, &x, &y);
 
       gtk_paint_layout (widget->style,
                         widget->window,
-                        GTK_WIDGET_STATE (widget),
+                        gtk_widget_get_state (widget),
                        FALSE,
                         &event->area,
                         widget,
@@ -3491,7 +4040,7 @@ gtk_label_expose (GtkWidget      *widget,
 
 
          state = GTK_STATE_SELECTED;
-         if (!GTK_WIDGET_HAS_FOCUS (widget))
+         if (!gtk_widget_has_focus (widget))
            state = GTK_STATE_ACTIVE;
 
           gdk_draw_layout_with_colors (widget->window,
@@ -3516,7 +4065,7 @@ gtk_label_expose (GtkWidget      *widget,
           GdkColor *link_color;
           GdkColor *visited_link_color;
 
-          if (info->selectable && GTK_WIDGET_HAS_FOCUS (widget))
+          if (info->selectable && gtk_widget_has_focus (widget))
            gtk_label_draw_cursor (label, x, y);
 
           focus_link = gtk_label_get_focus_link (label);
@@ -3555,7 +4104,7 @@ gtk_label_expose (GtkWidget      *widget,
               gdk_region_destroy (clip);
             }
 
-          if (focus_link && GTK_WIDGET_HAS_FOCUS (widget))
+          if (focus_link && gtk_widget_has_focus (widget))
             {
               range[0] = focus_link->start;
               range[1] = focus_link->end;
@@ -3566,7 +4115,7 @@ gtk_label_expose (GtkWidget      *widget,
                                                        1);
               gdk_region_get_clipbox (clip, &rect);
 
-              gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
+              gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
                                &event->area, widget, "label",
                                rect.x, rect.y, rect.width, rect.height);
 
@@ -3578,52 +4127,43 @@ gtk_label_expose (GtkWidget      *widget,
   return FALSE;
 }
 
-static void
-gtk_label_set_uline_text_internal (GtkLabel    *label,
-                                  const gchar *str)
+static gboolean
+separate_uline_pattern (const gchar  *str,
+                        guint        *accel_key,
+                        gchar       **new_str,
+                        gchar       **pattern)
 {
-  guint accel_key = GDK_VoidSymbol;
-
-  gchar *new_str;
-  gchar *pattern;
-  const gchar *src;
-  gchar *dest, *pattern_dest;
   gboolean underscore;
-      
-  g_return_if_fail (GTK_IS_LABEL (label));
-  g_return_if_fail (str != NULL);
+  const gchar *src;
+  gchar *dest;
+  gchar *pattern_dest;
+
+  *accel_key = GDK_VoidSymbol;
+  *new_str = g_new (gchar, strlen (str) + 1);
+  *pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
 
-  /* Split text into the base text and a separate pattern
-   * of underscores.
-   */
-  
-  new_str = g_new (gchar, strlen (str) + 1);
-  pattern = g_new (gchar, g_utf8_strlen (str, -1) + 1);
-  
   underscore = FALSE;
 
-  if (str == NULL)
-    str = "";
-  
   src = str;
-  dest = new_str;
-  pattern_dest = pattern;
-  
+  dest = *new_str;
+  pattern_dest = *pattern;
+
   while (*src)
     {
       gunichar c;
-      gchar *next_src;
+      const gchar *next_src;
 
       c = g_utf8_get_char (src);
       if (c == (gunichar)-1)
        {
          g_warning ("Invalid input string");
-         g_free (new_str);
-         g_free (pattern);
-         return;
+         g_free (*new_str);
+         g_free (*pattern);
+
+         return FALSE;
        }
       next_src = g_utf8_next_char (src);
-      
+
       if (underscore)
        {
          if (c == '_')
@@ -3631,13 +4171,13 @@ gtk_label_set_uline_text_internal (GtkLabel    *label,
          else
            {
              *pattern_dest++ = '_';
-             if (accel_key == GDK_VoidSymbol)
-               accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
+             if (*accel_key == GDK_VoidSymbol)
+               *accel_key = gdk_keyval_to_lower (gdk_unicode_to_keyval (c));
            }
 
          while (src < next_src)
            *dest++ = *src++;
-         
+
          underscore = FALSE;
        }
       else
@@ -3651,23 +4191,43 @@ gtk_label_set_uline_text_internal (GtkLabel    *label,
            {
              while (src < next_src)
                *dest++ = *src++;
-         
+
              *pattern_dest++ = ' ';
            }
        }
     }
+
   *dest = 0;
   *pattern_dest = 0;
-  
-  gtk_label_set_text_internal (label, new_str);
-  gtk_label_set_pattern_internal (label, pattern);
-  
-  g_free (pattern);
 
+  return TRUE;
+}
+
+static void
+gtk_label_set_uline_text_internal (GtkLabel    *label,
+                                  const gchar *str)
+{
+  guint accel_key = GDK_VoidSymbol;
+  gchar *new_str;
+  gchar *pattern;
+
+  g_return_if_fail (GTK_IS_LABEL (label));
+  g_return_if_fail (str != NULL);
+
+  /* Split text into the base text and a separate pattern
+   * of underscores.
+   */
+  if (!separate_uline_pattern (str, &accel_key, &new_str, &pattern))
+    return;
+
+  gtk_label_set_text_internal (label, new_str);
+  gtk_label_set_pattern_internal (label, pattern, TRUE);
   label->mnemonic_keyval = accel_key;
+
+  g_free (pattern);
 }
 
-guint      
+guint
 gtk_label_parse_uline (GtkLabel    *label,
                       const gchar *str)
 {
@@ -3841,34 +4401,32 @@ get_layout_index (GtkLabel *label,
   gint trailing = 0;
   const gchar *cluster;
   const gchar *cluster_end;
+  gboolean inside;
 
   *index = 0;
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
 
   window_to_layout_coords (label, &x, &y);
 
   x *= PANGO_SCALE;
   y *= PANGO_SCALE;
 
-  if (pango_layout_xy_to_index (label->layout,
-                                x, y,
-                                index, &trailing))
-    {
-      cluster = label->text + *index;
-      cluster_end = cluster;
-      while (trailing)
-        {
-          cluster_end = g_utf8_next_char (cluster_end);
-          --trailing;
-        }
-
-      *index += (cluster_end - cluster);
+  inside = pango_layout_xy_to_index (label->layout,
+                                     x, y,
+                                     index, &trailing);
 
-      return TRUE;
+  cluster = label->text + *index;
+  cluster_end = cluster;
+  while (trailing)
+    {
+      cluster_end = g_utf8_next_char (cluster_end);
+      --trailing;
     }
 
-  return FALSE;
+  *index += (cluster_end - cluster);
+
+  return inside;
 }
 
 static void
@@ -4069,7 +4627,7 @@ gtk_label_button_press (GtkWidget      *widget,
 
   if (event->button == 1)
     {
-      if (!GTK_WIDGET_HAS_FOCUS (widget))
+      if (!gtk_widget_has_focus (widget))
        {
          label->in_click = TRUE;
          gtk_widget_grab_focus (widget);
@@ -4180,6 +4738,38 @@ gtk_label_button_release (GtkWidget      *widget,
   return TRUE;
 }
 
+static void
+connect_mnemonics_visible_notify (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
+  GtkWidget *toplevel;
+  gboolean connected;
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
+
+  if (!GTK_IS_WINDOW (toplevel))
+    return;
+
+  /* always set up this widgets initial value */
+  priv->mnemonics_visible =
+    gtk_window_get_mnemonics_visible (GTK_WINDOW (toplevel));
+
+  connected =
+    GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toplevel),
+                                        "gtk-label-mnemonics-visible-connected"));
+
+  if (!connected)
+    {
+      g_signal_connect (toplevel,
+                        "notify::mnemonics-visible",
+                        G_CALLBACK (label_mnemonics_visible_changed),
+                        label);
+      g_object_set_data (G_OBJECT (toplevel),
+                         "gtk-label-mnemonics-visible-connected",
+                         GINT_TO_POINTER (1));
+    }
+}
+
 static void
 drag_begin_cb (GtkWidget      *widget,
                GdkDragContext *context,
@@ -4393,12 +4983,11 @@ gtk_label_create_window (GtkLabel *label)
   gint attributes_mask;
   
   g_assert (label->select_info);
-  g_assert (GTK_WIDGET_REALIZED (label));
+  widget = GTK_WIDGET (label);
+  g_assert (gtk_widget_get_realized (widget));
   
   if (label->select_info->window)
     return;
-  
-  widget = GTK_WIDGET (label);
 
   attributes.x = widget->allocation.x;
   attributes.y = widget->allocation.y;
@@ -4415,7 +5004,7 @@ gtk_label_create_window (GtkLabel *label)
     GDK_POINTER_MOTION_MASK      |
     GDK_POINTER_MOTION_HINT_MASK;
   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
-  if (GTK_WIDGET_IS_SENSITIVE (widget))
+  if (gtk_widget_is_sensitive (widget))
     {
       attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
                                                      GDK_XTERM);
@@ -4451,12 +5040,12 @@ gtk_label_ensure_select_info (GtkLabel *label)
     {
       label->select_info = g_new0 (GtkLabelSelectionInfo, 1);
 
-      GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
+      gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
 
-      if (GTK_WIDGET_REALIZED (label))
+      if (gtk_widget_get_realized (GTK_WIDGET (label)))
        gtk_label_create_window (label);
 
-      if (GTK_WIDGET_MAPPED (label))
+      if (gtk_widget_get_mapped (GTK_WIDGET (label)))
         gdk_window_show (label->select_info->window);
     }
 }
@@ -4474,7 +5063,7 @@ gtk_label_clear_select_info (GtkLabel *label)
       g_free (label->select_info);
       label->select_info = NULL;
 
-      GTK_WIDGET_UNSET_FLAGS (label, GTK_CAN_FOCUS);
+      gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
     }
 }
 
@@ -4852,15 +5441,15 @@ gtk_label_get_selection_bounds (GtkLabel  *label,
  * pixel positions, in combination with gtk_label_get_layout_offsets().
  * The returned layout is owned by the label so need not be
  * freed by the caller.
- * 
- * Return value: the #PangoLayout for this label
+ *
+ * Return value: (transfer none): the #PangoLayout for this label
  **/
 PangoLayout*
 gtk_label_get_layout (GtkLabel *label)
 {
   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
 
   return label->layout;
 }
@@ -4868,8 +5457,8 @@ gtk_label_get_layout (GtkLabel *label)
 /**
  * gtk_label_get_layout_offsets:
  * @label: a #GtkLabel
- * @x: location to store X offset of layout, or %NULL
- * @y: location to store Y offset of layout, or %NULL
+ * @x: (allow-none): location to store X offset of layout, or %NULL
+ * @y: (allow-none): location to store Y offset of layout, or %NULL
  *
  * Obtains the coordinates where the label will draw the #PangoLayout
  * representing the text in the label; useful to convert mouse events
@@ -4887,7 +5476,7 @@ gtk_label_get_layout_offsets (GtkLabel *label,
 {
   g_return_if_fail (GTK_IS_LABEL (label));
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
 
   get_layout_location (label, x, y);
 }
@@ -5032,7 +5621,7 @@ get_better_cursor (GtkLabel *label,
                "gtk-split-cursor", &split_cursor,
                NULL);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, FALSE);
   
   pango_layout_get_cursor_pos (label->layout, index,
                               &strong_pos, &weak_pos);
@@ -5072,7 +5661,7 @@ gtk_label_move_logically (GtkLabel *label,
       gint n_attrs;
       gint length;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       length = g_utf8_strlen (label->text, -1);
 
@@ -5116,7 +5705,7 @@ gtk_label_move_visually (GtkLabel *label,
       gboolean split_cursor;
       gboolean strong;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
 
       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
                    "gtk-split-cursor", &split_cursor,
@@ -5169,7 +5758,7 @@ gtk_label_move_forward_word (GtkLabel *label,
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
       
@@ -5197,7 +5786,7 @@ gtk_label_move_backward_word (GtkLabel *label,
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
       
@@ -5438,7 +6027,7 @@ popup_position_func (GtkMenu   *menu,
   label = GTK_LABEL (user_data);
   widget = GTK_WIDGET (label);
 
-  g_return_if_fail (GTK_WIDGET_REALIZED (label));
+  g_return_if_fail (gtk_widget_get_realized (widget));
 
   screen = gtk_widget_get_screen (widget);
   gdk_window_get_origin (widget->window, x, y);
@@ -5606,7 +6195,7 @@ gtk_label_rescan_links (GtkLabel *label)
   PangoAttrIterator *iter;
   GList *links;
 
-  if (!label->select_info)
+  if (!label->select_info || !label->select_info->links)
     return;
 
   attlist = pango_layout_get_attributes (layout);
@@ -5709,7 +6298,7 @@ gtk_label_activate_current_link (GtkLabel *label)
           if (window &&
               window->default_widget != widget &&
               !(widget == window->focus_widget &&
-                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
+                (!window->default_widget || !gtk_widget_is_sensitive (window->default_widget))))
             gtk_window_activate_default (window);
         }
     }
@@ -5740,7 +6329,7 @@ gtk_label_get_current_link (GtkLabel *label)
  * selectable label, the link in which the text cursor is currently
  * positioned.
  *
- * This function is intended for use in a #GtkLabel::link-activate handler
+ * This function is intended for use in a #GtkLabel::activate-link handler
  * or for use in a #GtkWidget::query-tooltip handler.
  *
  * Returns: the currently active URI. The string is owned by GTK+ and must