]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtklabel.c
texthandle: Set a bigger input shape, covering the line height
[~andy/gtk] / gtk / gtklabel.c
index c8e69fa43f3af0287c2e128557fad82d2bf6f844..bc17554a230aeeee329c64124acc5326400e12c4 100644 (file)
@@ -12,8 +12,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
  */
 
 /*
@@ -67,7 +66,7 @@
  *
  * The #GtkLabel widget displays a small amount of text. As the name
  * implies, most labels are used to label another widget such as a
- * #GtkButton, a #GtkMenuItem, or a #GtkOptionMenu.
+ * #GtkButton, a #GtkMenuItem, or a #GtkComboBox.
  *
  * <refsect2 id="GtkLabel-BUILDER-UI">
  * <title>GtkLabel as GtkBuildable</title>
@@ -243,7 +242,7 @@ struct _GtkLabelPrivate
   GtkWindow *mnemonic_window;
 
   PangoAttrList *attrs;
-  PangoAttrList *effective_attrs;
+  PangoAttrList *markup_attrs;
   PangoLayout   *layout;
 
   gchar   *label;
@@ -385,9 +384,6 @@ static void gtk_label_size_allocate     (GtkWidget        *widget,
                                          GtkAllocation    *allocation);
 static void gtk_label_state_flags_changed   (GtkWidget        *widget,
                                              GtkStateFlags     prev_state);
-static void gtk_label_style_updated     (GtkWidget        *widget);
-static void gtk_label_direction_changed (GtkWidget        *widget,
-                                        GtkTextDirection  previous_dir);
 static gint gtk_label_draw              (GtkWidget        *widget,
                                          cairo_t          *cr);
 static gboolean gtk_label_focus         (GtkWidget         *widget,
@@ -503,8 +499,8 @@ static gboolean      gtk_label_activate_link    (GtkLabel    *label,
 static void          gtk_label_activate_current_link (GtkLabel *label);
 static GtkLabelLink *gtk_label_get_current_link (GtkLabel  *label);
 static void          gtk_label_get_link_colors  (GtkWidget  *widget,
-                                                 GdkColor  **link_color,
-                                                 GdkColor  **visited_link_color);
+                                                 GdkColor   *link_color,
+                                                 GdkColor   *visited_link_color);
 static void          emit_activate_link         (GtkLabel     *label,
                                                  GtkLabelLink *link);
 
@@ -567,9 +563,7 @@ gtk_label_class_init (GtkLabelClass *class)
   widget_class->destroy = gtk_label_destroy;
   widget_class->size_allocate = gtk_label_size_allocate;
   widget_class->state_flags_changed = gtk_label_state_flags_changed;
-  widget_class->style_updated = gtk_label_style_updated;
   widget_class->query_tooltip = gtk_label_query_tooltip;
-  widget_class->direction_changed = gtk_label_direction_changed;
   widget_class->draw = gtk_label_draw;
   widget_class->realize = gtk_label_realize;
   widget_class->unrealize = gtk_label_unrealize;
@@ -841,7 +835,7 @@ gtk_label_class_init (GtkLabelClass *class)
    *
    * The preferred place to ellipsize the string, if the label does 
    * not have enough room to display the entire string, specified as a 
-   * #PangoEllisizeMode. 
+   * #PangoEllipsizeMode. 
    *
    * Note that setting this property to a value other than 
    * %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests 
@@ -1853,6 +1847,9 @@ gtk_label_screen_changed (GtkWidget *widget,
   GtkSettings *settings;
   gboolean shortcuts_connected;
 
+  /* The PangoContext is replaced when the screen changes, so clear the layouts */
+  gtk_label_clear_layout (GTK_LABEL (widget));
+
   if (!gtk_widget_has_screen (widget))
     return;
 
@@ -1966,9 +1963,9 @@ gtk_label_get_mnemonic_widget (GtkLabel *label)
  *
  * If the label has been set so that it has an mnemonic key this function
  * returns the keyval used for the mnemonic accelerator. If there is no
- * mnemonic set up it returns #GDK_VoidSymbol.
+ * mnemonic set up it returns #GDK_KEY_VoidSymbol.
  *
- * Returns: GDK keyval usable for accelerators, or #GDK_VoidSymbol
+ * Returns: GDK keyval usable for accelerators, or #GDK_KEY_VoidSymbol
  **/
 guint
 gtk_label_get_mnemonic_keyval (GtkLabel *label)
@@ -1980,15 +1977,18 @@ gtk_label_get_mnemonic_keyval (GtkLabel *label)
 
 static void
 gtk_label_set_text_internal (GtkLabel *label,
-                            gchar    *str)
+                             gchar    *str)
 {
   GtkLabelPrivate *priv = label->priv;
+  gboolean text_changed;
 
-  g_free (priv->text);
+  text_changed = g_strcmp0 (priv->text, str) != 0;
 
+  g_free (priv->text);
   priv->text = str;
 
-  gtk_label_select_region_index (label, 0, 0);
+  if (text_changed)
+    gtk_label_select_region_index (label, 0, 0);
 }
 
 static void
@@ -2034,38 +2034,19 @@ gtk_label_set_use_underline_internal (GtkLabel *label,
     }
 }
 
-static void
-gtk_label_compose_effective_attrs (GtkLabel *label)
+static gboolean
+my_pango_attr_list_merge_filter (PangoAttribute *attribute,
+                                 gpointer        list)
 {
-  GtkLabelPrivate      *priv = label->priv;
-  PangoAttrIterator *iter;
-  PangoAttribute    *attr;
-  GSList            *iter_attrs, *l;
+  pango_attr_list_change (list, pango_attribute_copy (attribute));
+  return FALSE;
+}
 
-  if (priv->attrs)
-    {
-      if (priv->effective_attrs)
-       {
-         if ((iter = pango_attr_list_get_iterator (priv->attrs)))
-           {
-             do
-               {
-                 iter_attrs = pango_attr_iterator_get_attrs (iter);
-                 for (l = iter_attrs; l; l = l->next)
-                   {
-                     attr = l->data;
-                     pango_attr_list_insert (priv->effective_attrs, attr);
-                   }
-                 g_slist_free (iter_attrs);
-               }
-             while (pango_attr_iterator_next (iter));
-             pango_attr_iterator_destroy (iter);
-           }
-       }
-      else
-       priv->effective_attrs =
-         pango_attr_list_ref (priv->attrs);
-    }
+static void
+my_pango_attr_list_merge (PangoAttrList *into,
+                          PangoAttrList *from)
+{
+  pango_attr_list_filter (from, my_pango_attr_list_merge_filter, into);
 }
 
 /* Calculates text, attrs and mnemonic_keyval from
@@ -2087,15 +2068,13 @@ gtk_label_recalculate (GtkLabel *label)
     {
       if (!priv->pattern_set)
         {
-          if (priv->effective_attrs)
-            pango_attr_list_unref (priv->effective_attrs);
-          priv->effective_attrs = NULL;
+          if (priv->markup_attrs)
+            pango_attr_list_unref (priv->markup_attrs);
+          priv->markup_attrs = NULL;
         }
       gtk_label_set_text_internal (label, g_strdup (priv->label));
     }
 
-  gtk_label_compose_effective_attrs (label);
-
   if (!priv->use_underline)
     priv->mnemonic_keyval = GDK_KEY_VoidSymbol;
 
@@ -2169,8 +2148,6 @@ gtk_label_set_attributes (GtkLabel         *label,
 
   g_object_notify (G_OBJECT (label), "attributes");
 
-  gtk_label_recalculate (label);
-
   gtk_label_clear_layout (label);
   gtk_widget_queue_resize (GTK_WIDGET (label));
 }
@@ -2246,8 +2223,6 @@ typedef struct
   GList *links;
   GString *new_str;
   gsize text_len;
-  GdkColor *link_color;
-  GdkColor *visited_link_color;
 } UriParserData;
 
 static void
@@ -2270,7 +2245,6 @@ start_element_handler (GMarkupParseContext  *context,
       gint line_number;
       gint char_number;
       gint i;
-      GdkColor *color = NULL;
 
       g_markup_parse_context_get_position (context, &line_number, &char_number);
 
@@ -2321,17 +2295,6 @@ start_element_handler (GMarkupParseContext  *context,
             }
         }
 
-      if (visited)
-        color = pdata->visited_link_color;
-      else
-        color = pdata->link_color;
-
-      g_string_append_printf (pdata->new_str,
-                              "<span color=\"#%04x%04x%04x\" underline=\"single\">",
-                              color->red,
-                              color->green,
-                              color->blue);
-
       link = g_new0 (GtkLabelLink, 1);
       link->uri = g_strdup (uri);
       link->title = g_strdup (title);
@@ -2378,7 +2341,6 @@ end_element_handler (GMarkupParseContext  *context,
     {
       GtkLabelLink *link = pdata->links->data;
       link->end = pdata->text_len;
-      g_string_append (pdata->new_str, "</span>");
     }
   else
     {
@@ -2428,21 +2390,33 @@ link_free (GtkLabelLink *link)
 }
 
 static void
-gtk_label_get_link_colors (GtkWidget  *widget,
-                           GdkColor  **link_color,
-                           GdkColor  **visited_link_color)
+gtk_label_get_link_colors (GtkWidget *widget,
+                           GdkColor  *link_color,
+                           GdkColor  *visited_link_color)
 {
   GtkStyleContext *context;
+  GdkColor *link, *visited;
 
   context = gtk_widget_get_style_context (widget);
   gtk_style_context_get_style (context,
-                               "link-color", link_color,
-                               "visited-link-color", visited_link_color,
+                               "link-color", &link,
+                               "visited-link-color", &visited,
                                 NULL);
-  if (!*link_color)
-    *link_color = gdk_color_copy (&default_link_color);
-  if (!*visited_link_color)
-    *visited_link_color = gdk_color_copy (&default_visited_link_color);
+  if (link)
+    {
+      *link_color = *link;
+      gdk_color_free (link);
+    }
+  else
+    *link_color = default_link_color;
+
+  if (visited)
+    {
+      *visited_link_color = *visited;
+      gdk_color_free (visited);
+    }
+  else
+    *visited_link_color = default_visited_link_color;
 }
 
 static gboolean
@@ -2467,8 +2441,6 @@ parse_uri_markup (GtkLabel     *label,
   pdata.new_str = g_string_sized_new (length);
   pdata.text_len = 0;
 
-  gtk_label_get_link_colors (GTK_WIDGET (label), &pdata.link_color, &pdata.visited_link_color);
-
   while (p != end && xml_isspace (*p))
     p++;
 
@@ -2500,17 +2472,12 @@ parse_uri_markup (GtkLabel     *label,
   *new_str = g_string_free (pdata.new_str, FALSE);
   *links = pdata.links;
 
-  gdk_color_free (pdata.link_color);
-  gdk_color_free (pdata.visited_link_color);
-
   return TRUE;
 
 failed:
   g_markup_parse_context_free (context);
   g_string_free (pdata.new_str, TRUE);
   g_list_free_full (pdata.links, (GDestroyNotify) link_free);
-  gdk_color_free (pdata.link_color);
-  gdk_color_free (pdata.visited_link_color);
 
   return FALSE;
 }
@@ -2614,9 +2581,9 @@ gtk_label_set_markup_internal (GtkLabel    *label,
 
   if (attrs)
     {
-      if (priv->effective_attrs)
-       pango_attr_list_unref (priv->effective_attrs);
-      priv->effective_attrs = attrs;
+      if (priv->markup_attrs)
+       pango_attr_list_unref (priv->markup_attrs);
+      priv->markup_attrs = attrs;
     }
 
   if (accel_char != 0)
@@ -2784,9 +2751,9 @@ gtk_label_set_pattern_internal (GtkLabel    *label,
   else
     attrs = gtk_label_pattern_to_attrs (label, pattern);
 
-  if (priv->effective_attrs)
-    pango_attr_list_unref (priv->effective_attrs);
-  priv->effective_attrs = attrs;
+  if (priv->markup_attrs)
+    pango_attr_list_unref (priv->markup_attrs);
+  priv->markup_attrs = attrs;
 }
 
 /**
@@ -3146,8 +3113,8 @@ gtk_label_finalize (GObject *object)
   if (priv->attrs)
     pango_attr_list_unref (priv->attrs);
 
-  if (priv->effective_attrs)
-    pango_attr_list_unref (priv->effective_attrs);
+  if (priv->markup_attrs)
+    pango_attr_list_unref (priv->markup_attrs);
 
   gtk_label_clear_links (label);
   g_free (priv->select_info);
@@ -3167,29 +3134,11 @@ gtk_label_clear_layout (GtkLabel *label)
     }
 }
 
-static PangoFontMetrics *
-get_font_metrics (PangoContext *context, GtkWidget *widget)
-{
-  GtkStyleContext *style_context;
-  const PangoFontDescription *font;
-  PangoFontMetrics *retval;
-
-  style_context = gtk_widget_get_style_context (widget);
-  font = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
-
-  retval = pango_context_get_metrics (context,
-                                      font,
-                                      pango_context_get_language (context));
-
-  return retval;
-}
-
 /**
  * gtk_label_get_measuring_layout:
  * @label: the label
  * @existing_layout: %NULL or an existing layout already in use.
  * @width: the width to measure with in pango units, or -1 for infinite
- * @height: the height to measure with in pango units, or -1 for infinite
  *
  * Gets a layout that can be used for measuring sizes. The returned
  * layout will be identical to the label's layout except for the
@@ -3201,8 +3150,7 @@ get_font_metrics (PangoContext *context, GtkWidget *widget)
 static PangoLayout *
 gtk_label_get_measuring_layout (GtkLabel *   label,
                                 PangoLayout *existing_layout,
-                                int          width,
-                                int          height)
+                                int          width)
 {
   GtkLabelPrivate *priv = label->priv;
   PangoRectangle rect;
@@ -3213,7 +3161,6 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
       if (existing_layout != priv->layout)
         {
           pango_layout_set_width (existing_layout, width);
-          pango_layout_set_height (existing_layout, height);
           return existing_layout;
         }
 
@@ -3222,8 +3169,7 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
 
   gtk_label_ensure_layout (label);
 
-  if (pango_layout_get_width (priv->layout) == width &&
-      pango_layout_get_height (priv->layout) == height)
+  if (pango_layout_get_width (priv->layout) == width)
     {
       g_object_ref (priv->layout);
       return priv->layout;
@@ -3237,7 +3183,6 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
     {
       g_object_ref (priv->layout);
       pango_layout_set_width (priv->layout, width);
-      pango_layout_set_height (priv->layout, height);
       return priv->layout;
     }
 
@@ -3248,7 +3193,6 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
    */
   pango_layout_get_extents (priv->layout, NULL, &rect);
   if ((width == -1 || rect.width <= width) &&
-      (height == -1 || rect.height <= height) &&
       !pango_layout_is_wrapped (priv->layout) &&
       !pango_layout_is_ellipsized (priv->layout))
     {
@@ -3258,7 +3202,6 @@ gtk_label_get_measuring_layout (GtkLabel *   label,
 
   copy = pango_layout_copy (priv->layout);
   pango_layout_set_width (copy, width);
-  pango_layout_set_height (copy, height);
   return copy;
 }
 
@@ -3272,14 +3215,14 @@ gtk_label_update_layout_width (GtkLabel *label)
 
   if (priv->ellipsize || priv->wrap)
     {
+      GtkBorder border;
       PangoRectangle logical;
-      gint xpad, ypad;
       gint width, height;
 
-      gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+      _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
-      width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - xpad * 2;
-      height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - ypad * 2;
+      width = gtk_widget_get_allocated_width (GTK_WIDGET (label)) - border.left - border.right;
+      height = gtk_widget_get_allocated_height (GTK_WIDGET (label)) - border.top - border.bottom;
 
       if (priv->have_transform)
         {
@@ -3289,7 +3232,6 @@ gtk_label_update_layout_width (GtkLabel *label)
           const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
 
           pango_layout_set_width (priv->layout, -1);
-          pango_layout_set_height (priv->layout, -1);
           pango_layout_get_pixel_extents (priv->layout, NULL, &logical);
 
           if (fabs (dy) < 0.01)
@@ -3343,13 +3285,11 @@ gtk_label_update_layout_width (GtkLabel *label)
       else
         {
           pango_layout_set_width (priv->layout, width * PANGO_SCALE);
-          pango_layout_set_height (priv->layout, priv->ellipsize ? height * PANGO_SCALE : -1);
         }
     }
   else
     {
       pango_layout_set_width (priv->layout, -1);
-      pango_layout_set_height (priv->layout, -1);
     }
 }
 
@@ -3367,6 +3307,7 @@ gtk_label_ensure_layout (GtkLabel *label)
   if (!priv->layout)
     {
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+      PangoAttrList *attrs;
       gdouble angle = gtk_label_get_angle (label);
 
       if (angle != 0.0 && !priv->select_info)
@@ -3393,8 +3334,63 @@ gtk_label_ensure_layout (GtkLabel *label)
 
       priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
 
-      if (priv->effective_attrs)
-       pango_layout_set_attributes (priv->layout, priv->effective_attrs);
+      if (priv->select_info && priv->select_info->links)
+        {
+          GdkColor link_color, visited_color;
+          PangoAttribute *attribute;
+          GList *list;
+          
+          gtk_label_get_link_colors (widget, &link_color, &visited_color);
+          attrs = pango_attr_list_new ();
+
+          for (list = priv->select_info->links; list; list = list->next)
+            {
+              GtkLabelLink *link = list->data;
+
+              attribute = pango_attr_underline_new (TRUE);
+              attribute->start_index = link->start;
+              attribute->end_index = link->end;
+              pango_attr_list_insert (attrs, attribute);
+
+              if (link->visited)
+                attribute = pango_attr_foreground_new (visited_color.red,
+                                                       visited_color.green,
+                                                       visited_color.blue);
+              else
+                attribute = pango_attr_foreground_new (link_color.red,
+                                                       link_color.green,
+                                                       link_color.blue);
+              attribute->start_index = link->start;
+              attribute->end_index = link->end;
+              pango_attr_list_insert (attrs, attribute);
+            }
+        }
+      else if (priv->markup_attrs || priv->attrs)
+        attrs = pango_attr_list_new ();
+      else
+        attrs = NULL;
+
+      if (priv->markup_attrs)
+        {
+          if (attrs)
+            my_pango_attr_list_merge (attrs, priv->markup_attrs);
+          else
+            attrs = pango_attr_list_ref (priv->markup_attrs);
+        }
+         
+      if (priv->attrs)
+        {
+          if (attrs)
+            my_pango_attr_list_merge (attrs, priv->attrs);
+          else
+            attrs = pango_attr_list_ref (priv->attrs);
+        }
+         
+      if (attrs)
+        {
+          pango_layout_set_attributes (priv->layout, attrs);
+          pango_attr_list_unref (attrs);
+        }
 
       switch (priv->jtype)
        {
@@ -3424,23 +3420,6 @@ 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 = get_font_metrics (context, widget);
-  ascent = pango_font_metrics_get_ascent (metrics);
-  descent = pango_font_metrics_get_descent (metrics);
-  pango_font_metrics_unref (metrics);
-
-  return ascent + descent;
-}
-
 static GtkSizeRequestMode
 gtk_label_get_request_mode (GtkWidget *widget)
 {
@@ -3462,11 +3441,10 @@ get_size_for_allocation (GtkLabel        *label,
                          gint            *minimum_size,
                          gint            *natural_size)
 {
-  GtkLabelPrivate *priv = label->priv;
   PangoLayout *layout;
   gint text_height;
 
-  layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE, -1);
+  layout = gtk_label_get_measuring_layout (label, NULL, allocation * PANGO_SCALE);
 
   pango_layout_get_pixel_size (layout, NULL, &text_height);
 
@@ -3474,15 +3452,7 @@ get_size_for_allocation (GtkLabel        *label,
     *minimum_size = text_height;
 
   if (natural_size)
-    {
-      if (priv->ellipsize && priv->wrap)
-        {
-          layout = gtk_label_get_measuring_layout (label, layout, allocation * PANGO_SCALE, G_MAXINT);
-          pango_layout_get_pixel_size (layout, NULL, &text_height);
-        }
-
-      *natural_size = text_height;
-    }
+    *natural_size = text_height;
 
   g_object_unref (layout);
 }
@@ -3496,7 +3466,9 @@ get_char_pixels (GtkWidget   *label,
   gint char_width, digit_width;
 
   context = pango_layout_get_context (layout);
-  metrics = get_font_metrics (context, GTK_WIDGET (label));
+  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);
@@ -3506,11 +3478,12 @@ get_char_pixels (GtkWidget   *label,
 
 static void
 gtk_label_get_preferred_layout_size (GtkLabel *label,
-                                     PangoRectangle *required,
-                                     PangoRectangle *natural)
+                                     PangoRectangle *smallest,
+                                     PangoRectangle *widest)
 {
   GtkLabelPrivate *priv = label->priv;
   PangoLayout *layout;
+  gint char_pixels;
 
   /* "width-chars" Hard-coded minimum width:
    *    - minimum size should be MAX (width-chars, strlen ("..."));
@@ -3529,46 +3502,46 @@ gtk_label_get_preferred_layout_size (GtkLabel *label,
    */
 
   /* Start off with the pixel extents of an as-wide-as-possible layout */
-  layout = gtk_label_get_measuring_layout (label, NULL, -1, -1);
+  layout = gtk_label_get_measuring_layout (label, NULL, -1);
 
-  pango_layout_get_extents (layout, NULL, natural);
-  natural->x = natural->y = 0;
-
-  if (priv->wrap)
-    natural->height = get_single_line_height (GTK_WIDGET (label), layout);
+  if (priv->width_chars > -1 || priv->max_width_chars > -1)
+    char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
+  else
+    char_pixels = 0;
+      
+  pango_layout_get_extents (layout, NULL, widest);
+  widest->width = MAX (widest->width, char_pixels * priv->width_chars);
+  widest->x = widest->y = 0;
 
   if (priv->ellipsize || priv->wrap)
     {
       /* a layout with width 0 will be as small as humanly possible */
-      layout = gtk_label_get_measuring_layout (label, layout, 0, -1);
+      layout = gtk_label_get_measuring_layout (label,
+                                               layout,
+                                               priv->width_chars > -1 ? char_pixels * priv->width_chars
+                                                                      : 0);
 
-      pango_layout_get_extents (layout, NULL, required);
+      pango_layout_get_extents (layout, NULL, smallest);
+      smallest->width = MAX (smallest->width, char_pixels * priv->width_chars);
+      smallest->x = smallest->y = 0;
 
-      /* can happen when Pango decides to ellipsize text */
-      if (required->width > natural->width)
-        required->width = natural->width;
-
-      required->x = required->y = 0;
-      required->height = natural->height;
+      if (priv->max_width_chars > -1 && widest->width > char_pixels * priv->max_width_chars)
+        {
+          layout = gtk_label_get_measuring_layout (label,
+                                                   layout,
+                                                   MAX (smallest->width, char_pixels * priv->max_width_chars));
+          pango_layout_get_extents (layout, NULL, widest);
+          widest->width = MAX (widest->width, char_pixels * priv->width_chars);
+          widest->x = widest->y = 0;
+        }
     }
   else
     {
-      *required = *natural;
+      *smallest = *widest;
     }
 
-  if (priv->width_chars > -1 || priv->max_width_chars > -1)
-    {
-      gint char_pixels;
-
-      char_pixels = get_char_pixels (GTK_WIDGET (label), layout);
-      
-      if (priv->width_chars > -1)
-        required->width = MAX (required->width, char_pixels * priv->width_chars);
-
-      if (priv->max_width_chars > -1)
-        natural->width = MIN (natural->width, priv->max_width_chars * char_pixels);
-      natural->width = MAX (natural->width, required->width);
-    }
+  if (widest->width < smallest->width)
+    *smallest = *widest;
 
   g_object_unref (layout);
 }
@@ -3581,36 +3554,28 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
 {
   GtkLabel      *label = GTK_LABEL (widget);
   GtkLabelPrivate  *priv = label->priv;
-  PangoRectangle required_rect;
-  PangoRectangle natural_rect;
-  gint           xpad, ypad;
+  PangoRectangle widest_rect;
+  PangoRectangle smallest_rect;
+  GtkBorder border;
 
-  gtk_label_get_preferred_layout_size (label, &required_rect, &natural_rect);
+  gtk_label_get_preferred_layout_size (label, &smallest_rect, &widest_rect);
 
   /* Now that we have minimum and natural sizes in pango extents, apply a possible transform */
   if (priv->have_transform)
     {
-      PangoLayout *copy;
       PangoContext *context;
       const PangoMatrix *matrix;
 
-      copy = pango_layout_copy (priv->layout);
-      context = pango_layout_get_context (copy);
+      context = pango_layout_get_context (priv->layout);
       matrix = pango_context_get_matrix (context);
 
-      pango_layout_set_width (copy, -1);
-      pango_layout_set_ellipsize (copy, PANGO_ELLIPSIZE_NONE);
-
-      pango_layout_get_extents (copy, NULL, &natural_rect);
-      g_object_unref (copy);
-
-      pango_matrix_transform_rectangle (matrix, &required_rect);
-      pango_matrix_transform_rectangle (matrix, &natural_rect);
+      pango_matrix_transform_rectangle (matrix, &widest_rect);
+      pango_matrix_transform_rectangle (matrix, &smallest_rect);
 
-      /* Bump the natural size in case of ellipsize to ensure pango has
+      /* Bump the size in case of ellipsize to ensure pango has
        * enough space in the angles (note, we could alternatively set the
        * layout to not ellipsize when we know we have been allocated our
-       * full natural size, or it may be that pango needs a fix here).
+       * full size, or it may be that pango needs a fix here).
        */
       if (priv->ellipsize && priv->angle != 0 && priv->angle != 90 && 
           priv->angle != 180 && priv->angle != 270 && priv->angle != 360)
@@ -3618,18 +3583,20 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
           /* For some reason we only need this at about 110 degrees, and only
            * when gaining in height
            */
-          natural_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
-          natural_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          widest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          widest_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          smallest_rect.height += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
+          smallest_rect.width  += ROTATION_ELLIPSIZE_PADDING * 2 * PANGO_SCALE;
         }
     }
 
-  required_rect.width  = PANGO_PIXELS_CEIL (required_rect.width);
-  required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+  widest_rect.width  = PANGO_PIXELS_CEIL (widest_rect.width);
+  widest_rect.height = PANGO_PIXELS_CEIL (widest_rect.height);
 
-  natural_rect.width  = PANGO_PIXELS_CEIL (natural_rect.width);
-  natural_rect.height = PANGO_PIXELS_CEIL (natural_rect.height);
+  smallest_rect.width  = PANGO_PIXELS_CEIL (smallest_rect.width);
+  smallest_rect.height = PANGO_PIXELS_CEIL (smallest_rect.height);
 
-  gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+  _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
   if (orientation == GTK_ORIENTATION_HORIZONTAL)
     {
@@ -3644,19 +3611,19 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
            */
           get_size_for_allocation (label,
                                    GTK_ORIENTATION_VERTICAL,
-                                   required_rect.height,
+                                   smallest_rect.height,
                                    minimum_size, natural_size);
 
         }
       else
         {
           /* Normal desired width */
-          *minimum_size = required_rect.width;
-          *natural_size = natural_rect.width;
+          *minimum_size = smallest_rect.width;
+          *natural_size = widest_rect.width;
         }
 
-      *minimum_size += xpad * 2;
-      *natural_size += xpad * 2;
+      *minimum_size += border.left + border.right;
+      *natural_size += border.left + border.right;
     }
   else /* GTK_ORIENTATION_VERTICAL */
     {
@@ -3671,7 +3638,7 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
            */
           get_size_for_allocation (label,
                                    GTK_ORIENTATION_HORIZONTAL,
-                                   required_rect.width,
+                                   widest_rect.width,
                                    minimum_size, natural_size);
         }
       else
@@ -3679,16 +3646,15 @@ gtk_label_get_preferred_size (GtkWidget      *widget,
           /* A vertically rotated label does w4h, so return the base
            * desired height (text length)
            */
-          *minimum_size = required_rect.height;
-          *natural_size = natural_rect.height;
+          *minimum_size = MIN (smallest_rect.height, widest_rect.height);
+          *natural_size = MAX (smallest_rect.height, widest_rect.height);
         }
 
-      *minimum_size += ypad * 2;
-      *natural_size += ypad * 2;
+      *minimum_size += border.top + border.bottom;
+      *natural_size += border.top + border.bottom;
     }
 }
 
-
 static void
 gtk_label_get_preferred_width (GtkWidget *widget,
                                gint      *minimum_size,
@@ -3716,22 +3682,22 @@ gtk_label_get_preferred_width_for_height (GtkWidget *widget,
 
   if (priv->wrap && (priv->angle == 90 || priv->angle == 270))
     {
-      gint xpad, ypad;
+      GtkBorder border;
 
-      gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+      _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
       if (priv->wrap)
         gtk_label_clear_layout (label);
 
       get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
-                               MAX (1, height - (ypad * 2)),
+                               MAX (1, height - border.top - border.bottom),
                                minimum_width, natural_width);
 
       if (minimum_width)
-        *minimum_width += xpad * 2;
+        *minimum_width += border.right + border.left;
 
       if (natural_width)
-        *natural_width += xpad * 2;
+        *natural_width += border.right + border.left;
     }
   else
     GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
@@ -3748,22 +3714,22 @@ gtk_label_get_preferred_height_for_width (GtkWidget *widget,
 
   if (priv->wrap && (priv->angle == 0 || priv->angle == 180 || priv->angle == 360))
     {
-      gint xpad, ypad;
+      GtkBorder border;
 
-      gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
+      _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
       if (priv->wrap)
         gtk_label_clear_layout (label);
 
       get_size_for_allocation (label, GTK_ORIENTATION_HORIZONTAL,
-                               MAX (1, width - xpad * 2),
+                               MAX (1, width - border.left - border.right),
                                minimum_height, natural_height);
 
       if (minimum_height)
-        *minimum_height += ypad * 2;
+        *minimum_height += border.top + border.bottom;
 
       if (natural_height)
-        *natural_height += ypad * 2;
+        *natural_height += border.top + border.bottom;
     }
   else
     GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
@@ -3843,37 +3809,10 @@ gtk_label_state_flags_changed (GtkWidget     *widget,
       gtk_label_update_cursor (label);
     }
 
-  /* We have to clear the layout, fonts etc. may have changed */
-  gtk_label_clear_layout (label);
-
   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
     GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed (widget, prev_state);
 }
 
-static void
-gtk_label_style_updated (GtkWidget *widget)
-{
-  GtkLabel *label = GTK_LABEL (widget);
-
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->style_updated (widget);
-
-  /* We have to clear the layout, fonts etc. may have changed */
-  gtk_label_clear_layout (label);
-}
-
-static void 
-gtk_label_direction_changed (GtkWidget        *widget,
-                            GtkTextDirection previous_dir)
-{
-  GtkLabel *label = GTK_LABEL (widget);
-  GtkLabelPrivate *priv = label->priv;
-
-  if (priv->layout)
-    pango_layout_context_changed (priv->layout);
-
-  GTK_WIDGET_CLASS (gtk_label_parent_class)->direction_changed (widget, previous_dir);
-}
-
 static void
 get_layout_location (GtkLabel  *label,
                      gint      *xp,
@@ -3883,9 +3822,9 @@ get_layout_location (GtkLabel  *label,
   GtkMisc *misc;
   GtkWidget *widget;
   GtkLabelPrivate *priv;
+  GtkBorder border;
   gint req_width, x, y;
   gint req_height;
-  gint xpad, ypad;
   gfloat xalign, yalign;
   PangoRectangle logical;
 
@@ -3894,7 +3833,7 @@ get_layout_location (GtkLabel  *label,
   priv   = label->priv;
 
   gtk_misc_get_alignment (misc, &xalign, &yalign);
-  gtk_misc_get_padding (misc, &xpad, &ypad);
+  _gtk_misc_get_padding_and_border (GTK_MISC (label), &border);
 
   if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
     xalign = 1.0 - xalign;
@@ -3913,13 +3852,12 @@ get_layout_location (GtkLabel  *label,
   req_width  = logical.width;
   req_height = logical.height;
 
-  req_width  += 2 * xpad;
-  req_height += 2 * ypad;
+  req_width  += border.left + border.right;
+  req_height += border.top + border.bottom;
 
   gtk_widget_get_allocation (widget, &allocation);
 
-  x = floor (allocation.x + xpad + xalign * (allocation.width - req_width) - logical.x);
-
+  x = floor (allocation.x + border.left + xalign * (allocation.width - req_width) - logical.x);
 
   /* bgo#315462 - For single-line labels, *do* align the requisition with
    * respect to the allocation, even if we are under-allocated.  For multi-line
@@ -3935,9 +3873,9 @@ get_layout_location (GtkLabel  *label,
    *   middle".  You want to read the first line, at least, to get some context.
    */
   if (pango_layout_get_line_count (priv->layout) == 1)
-    y = floor (allocation.y + ypad + (allocation.height - req_height) * yalign) - logical.y;
+    y = floor (allocation.y + border.top + (allocation.height - req_height) * yalign) - logical.y;
   else
-    y = floor (allocation.y + ypad + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
+    y = floor (allocation.y + border.top + MAX ((allocation.height - req_height) * yalign, 0)) - logical.y;
 
   if (xp)
     *xp = x;
@@ -4012,13 +3950,20 @@ gtk_label_draw (GtkWidget *widget,
 
   gtk_label_ensure_layout (label);
 
+  context = gtk_widget_get_style_context (widget);
+  gtk_widget_get_allocation (widget, &allocation);
+
+  gtk_render_background (context, cr,
+                         0, 0,
+                         allocation.width, allocation.height);
+  gtk_render_frame (context, cr,
+                    0, 0,
+                    allocation.width, allocation.height);
+
   if (priv->text && (*priv->text != '\0'))
     {
       get_layout_location (label, &x, &y);
 
-      context = gtk_widget_get_style_context (widget);
-      gtk_widget_get_allocation (widget, &allocation);
-
       cairo_translate (cr, -allocation.x, -allocation.y);
 
       gtk_render_layout (context, cr,
@@ -4078,8 +4023,8 @@ gtk_label_draw (GtkWidget *widget,
           cairo_region_t *clip;
           GdkRectangle rect;
           GdkColor *text_color;
-          GdkColor *link_color;
-          GdkColor *visited_link_color;
+          GdkColor link_color;
+          GdkColor visited_link_color;
 
           if (info->selectable &&
               gtk_widget_has_focus (widget) &&
@@ -4116,9 +4061,9 @@ gtk_label_draw (GtkWidget *widget,
 
               gtk_label_get_link_colors (widget, &link_color, &visited_link_color);
               if (active_link->visited)
-                text_color = visited_link_color;
+                text_color = &visited_link_color;
               else
-                text_color = link_color;
+                text_color = &link_color;
 
               if (info->link_clicked)
                 state |= GTK_STATE_FLAG_ACTIVE;
@@ -4130,13 +4075,12 @@ gtk_label_draw (GtkWidget *widget,
               gdk_cairo_set_source_rgba (cr, &bg_color);
               cairo_paint (cr);
 
-              gdk_cairo_set_source_color (cr, text_color);
+              cairo_set_source_rgb (cr, text_color->red / 65535., 
+                                        text_color->green / 65535.,
+                                        text_color->blue / 65535.);
               cairo_move_to (cr, x, y);
               _gtk_pango_fill_layout (cr, priv->layout);
 
-              gdk_color_free (link_color);
-              gdk_color_free (visited_link_color);
-
               cairo_restore (cr);
             }
 
@@ -4621,7 +4565,7 @@ gtk_label_button_press (GtkWidget      *widget,
           gtk_label_do_popup (label, event);
           return TRUE;
         }
-      else if (event->button == 1)
+      else if (event->button == GDK_BUTTON_PRIMARY)
         {
           info->link_clicked = 1;
           gtk_widget_queue_draw (widget);
@@ -4640,7 +4584,7 @@ gtk_label_button_press (GtkWidget      *widget,
 
       return TRUE;
     }
-  else if (event->button == 1)
+  else if (event->button == GDK_BUTTON_PRIMARY)
     {
       if (!gtk_widget_has_focus (widget))
         {
@@ -4741,7 +4685,7 @@ gtk_label_button_release (GtkWidget      *widget,
       return FALSE;
     }
 
-  if (event->button != 1)
+  if (event->button != GDK_BUTTON_PRIMARY)
     return FALSE;
 
   if (info->active_link &&
@@ -5038,7 +4982,7 @@ gtk_label_create_window (GtkLabel *label)
 
   priv->select_info->window = gdk_window_new (gtk_widget_get_window (widget),
                                                &attributes, attributes_mask);
-  gdk_window_set_user_data (priv->select_info->window, widget);
+  gtk_widget_register_window (widget, priv->select_info->window);
 
   if (attributes_mask & GDK_WA_CURSOR)
     g_object_unref (attributes.cursor);
@@ -5054,7 +4998,7 @@ gtk_label_destroy_window (GtkLabel *label)
   if (priv->select_info->window == NULL)
     return;
 
-  gdk_window_set_user_data (priv->select_info->window, NULL);
+  gtk_widget_unregister_window (GTK_WIDGET (label), priv->select_info->window);
   gdk_window_destroy (priv->select_info->window);
   priv->select_info->window = NULL;
 }
@@ -6294,7 +6238,7 @@ emit_activate_link (GtkLabel     *label,
     {
       link->visited = TRUE;
       /* FIXME: shouldn't have to redo everything here */
-      gtk_label_recalculate (label);
+      gtk_label_clear_layout (label);
     }
 }