12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/styled_label.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <vector>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
95e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/font_list.h"
1158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/gfx/text_elider.h"
127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "ui/native_theme/native_theme.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/label.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/link.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/styled_label_listener.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace views {
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Helpers --------------------------------------------------------------------
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Calculates the height of a line of text. Currently returns the height of
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// a label.
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)int CalculateLineHeight(const gfx::FontList& font_list) {
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Label label;
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  label.SetFontList(font_list);
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return label.GetPreferredSize().height();
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochscoped_ptr<Label> CreateLabelRange(
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& text,
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const gfx::FontList& font_list,
357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    const StyledLabel::RangeStyleInfo& style_info,
367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    views::LinkListener* link_listener) {
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<Label> result;
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (style_info.is_link) {
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Link* link = new Link(text);
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    link->set_listener(link_listener);
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    link->SetUnderline((style_info.font_style & gfx::Font::UNDERLINE) != 0);
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    result.reset(link);
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    result.reset(new Label(text));
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  result->SetEnabledColor(style_info.color);
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result->SetFontList(font_list);
507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!style_info.tooltip.empty())
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    result->SetTooltipText(style_info.tooltip);
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (style_info.font_style != gfx::Font::NORMAL) {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    result->SetFontList(
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        result->font_list().DeriveWithStyle(style_info.font_style));
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return result.Pass();
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StyledLabel::RangeStyleInfo ------------------------------------------------
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)StyledLabel::RangeStyleInfo::RangeStyleInfo()
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : font_style(gfx::Font::NORMAL),
687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      color(ui::NativeTheme::instance()->GetSystemColor(
697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          ui::NativeTheme::kColorId_LabelEnabledColor)),
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      disable_line_wrapping(false),
71ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      is_link(false) {}
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)StyledLabel::RangeStyleInfo::~RangeStyleInfo() {}
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)StyledLabel::RangeStyleInfo StyledLabel::RangeStyleInfo::CreateForLink() {
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RangeStyleInfo result;
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  result.disable_line_wrapping = true;
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  result.is_link = true;
807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  result.color = Link::GetDefaultEnabledColor();
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return result;
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StyledLabel::StyleRange ----------------------------------------------------
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool StyledLabel::StyleRange::operator<(
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const StyledLabel::StyleRange& other) const {
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return range.start() < other.range.start();
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// StyledLabel ----------------------------------------------------------------
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)StyledLabel::StyledLabel(const base::string16& text,
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         StyledLabelListener* listener)
97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : specified_line_height_(0),
98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      listener_(listener),
9958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      displayed_on_background_color_set_(false),
10058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      auto_color_readability_enabled_(true) {
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::TrimWhitespace(text, base::TRIM_TRAILING, &text_);
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)StyledLabel::~StyledLabel() {}
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StyledLabel::SetText(const base::string16& text) {
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  text_ = text;
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  style_ranges_.clear();
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RemoveAllChildViews(true);
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  PreferredSizeChanged();
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StyledLabel::SetBaseFontList(const gfx::FontList& font_list) {
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  font_list_ = font_list;
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PreferredSizeChanged();
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void StyledLabel::AddStyleRange(const gfx::Range& range,
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                const RangeStyleInfo& style_info) {
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(!range.is_reversed());
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(!range.is_empty());
12258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(gfx::Range(0, text_.size()).Contains(range));
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Insert the new range in sorted order.
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StyleRanges new_range;
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  new_range.push_front(StyleRange(range, style_info));
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  style_ranges_.merge(new_range);
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
129ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  PreferredSizeChanged();
130ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
131ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
132ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochvoid StyledLabel::SetDefaultStyle(const RangeStyleInfo& style_info) {
133ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  default_style_info_ = style_info;
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  PreferredSizeChanged();
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
137116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid StyledLabel::SetLineHeight(int line_height) {
138116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  specified_line_height_ = line_height;
139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  PreferredSizeChanged();
140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid StyledLabel::SetDisplayedOnBackgroundColor(SkColor color) {
1437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  displayed_on_background_color_ = color;
1447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  displayed_on_background_color_set_ = true;
1457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
1467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Insets StyledLabel::GetInsets() const {
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Insets insets = View::GetInsets();
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // We need a focus border iff we contain a link that will have a focus border.
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // That in turn will be true only if the link is non-empty.
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (StyleRanges::const_iterator i(style_ranges_.begin());
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        i != style_ranges_.end(); ++i) {
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (i->style_info.is_link && !i->range.is_empty()) {
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      const gfx::Insets focus_border_padding(
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          Label::kFocusBorderPadding, Label::kFocusBorderPadding,
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          Label::kFocusBorderPadding, Label::kFocusBorderPadding);
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      insets += focus_border_padding;
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return insets;
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)int StyledLabel::GetHeightForWidth(int w) const {
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (w != calculated_size_.width()) {
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // TODO(erg): Munge the const-ness of the style label. CalculateAndDoLayout
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // doesn't actually make any changes to member variables when |dry_run| is
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // set to true. In general, the mutating and non-mutating parts shouldn't
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // be in the same codepath.
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    calculated_size_ =
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        const_cast<StyledLabel*>(this)->CalculateAndDoLayout(w, true);
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return calculated_size_.height();
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StyledLabel::Layout() {
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  calculated_size_ = CalculateAndDoLayout(GetLocalBounds().width(), false);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochvoid StyledLabel::PreferredSizeChanged() {
183ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  calculated_size_ = gfx::Size();
184ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  View::PreferredSizeChanged();
185ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
186ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StyledLabel::LinkClicked(Link* source, int event_flags) {
1887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (listener_)
1897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    listener_->StyledLabelLinkClicked(link_targets_[source], event_flags);
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)gfx::Size StyledLabel::CalculateAndDoLayout(int width, bool dry_run) {
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!dry_run) {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RemoveAllChildViews(true);
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    link_targets_.clear();
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  width -= GetInsets().width();
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (width <= 0 || text_.empty())
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return gfx::Size();
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
202116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  const int line_height = specified_line_height_ > 0 ? specified_line_height_
203116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      : CalculateLineHeight(font_list_);
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The index of the line we're on.
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int line = 0;
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The x position (in pixels) of the line we're on, relative to content
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // bounds.
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int x = 0;
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 remaining_string = text_;
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StyleRanges::const_iterator current_range = style_ranges_.begin();
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Iterate over the text, creating a bunch of labels and links and laying them
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // out in the appropriate positions.
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (!remaining_string.empty()) {
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Don't put whitespace at beginning of a line with an exception for the
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // first line (so the text's leading whitespace is respected).
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (x == 0 && line > 0) {
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::TrimWhitespace(remaining_string, base::TRIM_LEADING,
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                           &remaining_string);
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
22358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    gfx::Range range(gfx::Range::InvalidRange());
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (current_range != style_ranges_.end())
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      range = current_range->range;
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const size_t position = text_.size() - remaining_string.size();
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height);
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16> substrings;
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    gfx::FontList text_font_list = font_list_;
232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // If the start of the remaining text is inside a styled range, the font
233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // style may differ from the base font. The font specified by the range
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // should be used when eliding text.
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (position >= range.start()) {
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      text_font_list = text_font_list.DeriveWithStyle(
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          current_range->style_info.font_style);
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
23958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    gfx::ElideRectangleText(remaining_string,
240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            text_font_list,
241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            chunk_bounds.width(),
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            chunk_bounds.height(),
243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            gfx::IGNORE_LONG_WORDS,
244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                            &substrings);
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK(!substrings.empty());
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::string16 chunk = substrings[0];
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (chunk.empty()) {
249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Nothing fits on this line. Start a new line.
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // If x is 0, first line may have leading whitespace that doesn't fit in a
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // single line, so try trimming those. Otherwise there is no room for
252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // anything; abort.
253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (x == 0) {
254c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (line == 0) {
255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          base::TrimWhitespace(remaining_string, base::TRIM_LEADING,
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                               &remaining_string);
257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          continue;
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      x = 0;
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      line++;
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    scoped_ptr<Label> label;
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (position >= range.start()) {
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      const RangeStyleInfo& style_info = current_range->style_info;
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (style_info.disable_line_wrapping && chunk.size() < range.length() &&
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          position == range.start() && x != 0) {
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // If the chunk should not be wrapped, try to fit it entirely on the
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        // next line.
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        x = 0;
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        line++;
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue;
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      chunk = chunk.substr(0, std::min(chunk.size(), range.end() - position));
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      label = CreateLabelRange(chunk, font_list_, style_info, this);
283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
284c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (style_info.is_link && !dry_run)
2857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        link_targets_[label.get()] = range;
286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (position + chunk.size() >= range.end())
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ++current_range;
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // This chunk is normal text.
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (position + chunk.size() > range.start())
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        chunk = chunk.substr(0, range.start() - position);
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      label = CreateLabelRange(chunk, font_list_, default_style_info_, this);
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (displayed_on_background_color_set_)
2977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      label->SetBackgroundColor(displayed_on_background_color_);
29858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    label->SetAutoColorReadabilityEnabled(auto_color_readability_enabled_);
2997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Calculate the size of the optional focus border, and overlap by that
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // amount. Otherwise, "<a>link</a>," will render as "link ,".
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    gfx::Insets focus_border_insets(label->GetInsets());
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    focus_border_insets += -label->View::GetInsets();
3047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    const gfx::Size view_size = label->GetPreferredSize();
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!dry_run) {
3067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      label->SetBoundsRect(gfx::Rect(
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          gfx::Point(GetInsets().left() + x - focus_border_insets.left(),
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     GetInsets().top() + line * line_height -
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         focus_border_insets.top()),
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          view_size));
3117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      AddChildView(label.release());
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    x += view_size.width() - focus_border_insets.width();
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remaining_string = remaining_string.substr(chunk.size());
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
318116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // The user-specified line height only applies to interline spacing, so the
319116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // final line's height is unaffected.
320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  int total_height = line * line_height +
321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      CalculateLineHeight(font_list_) + GetInsets().height();
322116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return gfx::Size(width, total_height);
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace views
326