// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/button/text_button.h"

#include <algorithm>

#include "base/logging.h"
#include "grit/ui_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/painter.h"
#include "ui/views/widget/widget.h"

#if defined(OS_WIN)
#include "skia/ext/skia_utils_win.h"
#include "ui/gfx/platform_font_win.h"
#include "ui/native_theme/native_theme_win.h"
#endif

namespace views {

namespace {

// Default space between the icon and text.
const int kDefaultIconTextSpacing = 5;

// Preferred padding between text and edge.
const int kPreferredPaddingHorizontal = 6;
const int kPreferredPaddingVertical = 5;

// Preferred padding between text and edge for NativeTheme border.
const int kPreferredNativeThemePaddingHorizontal = 12;
const int kPreferredNativeThemePaddingVertical = 5;

// By default the focus rect is drawn at the border of the view.  For a button,
// we inset the focus rect by 3 pixels so that it doesn't draw on top of the
// button's border. This roughly matches how the Windows native focus rect for
// buttons looks. A subclass that draws a button with different padding may need
// to provide a different focus painter and do something different.
const int kFocusRectInset = 3;

// How long the hover fade animation should last.
const int kHoverAnimationDurationMs = 170;

#if defined(OS_WIN)
// These sizes are from http://msdn.microsoft.com/en-us/library/aa511279.aspx
const int kMinWidthDLUs = 50;
const int kMinHeightDLUs = 14;
#endif

// The default hot and pushed button image IDs; normal has none by default.
const int kHotImages[] = IMAGE_GRID(IDR_TEXTBUTTON_HOVER);
const int kPushedImages[] = IMAGE_GRID(IDR_TEXTBUTTON_PRESSED);

}  // namespace

// static
const char TextButtonBase::kViewClassName[] = "TextButtonBase";
// static
const char TextButton::kViewClassName[] = "TextButton";


// TextButtonBorder -----------------------------------------------------------

TextButtonBorder::TextButtonBorder() {
}

TextButtonBorder::~TextButtonBorder() {
}

void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) {
}

gfx::Insets TextButtonBorder::GetInsets() const {
  return insets_;
}

gfx::Size TextButtonBorder::GetMinimumSize() const {
  return gfx::Size();
}

void TextButtonBorder::SetInsets(const gfx::Insets& insets) {
  insets_ = insets;
}

TextButtonBorder* TextButtonBorder::AsTextButtonBorder() {
  return this;
}

const TextButtonBorder* TextButtonBorder::AsTextButtonBorder() const {
  return this;
}


// TextButtonDefaultBorder ----------------------------------------------------

TextButtonDefaultBorder::TextButtonDefaultBorder()
    : vertical_padding_(kPreferredPaddingVertical) {
  set_hot_painter(Painter::CreateImageGridPainter(kHotImages));
  set_pushed_painter(Painter::CreateImageGridPainter(kPushedImages));
  SetInsets(gfx::Insets(vertical_padding_, kPreferredPaddingHorizontal,
                        vertical_padding_, kPreferredPaddingHorizontal));
}

TextButtonDefaultBorder::~TextButtonDefaultBorder() {
}

void TextButtonDefaultBorder::Paint(const View& view, gfx::Canvas* canvas) {
  const TextButton* button = static_cast<const TextButton*>(&view);
  int state = button->state();
  bool animating = button->GetAnimation()->is_animating();

  Painter* painter = normal_painter_.get();
  // Use the hot painter when we're hovered. Also use the hot painter when we're
  // STATE_NORMAL and |animating| so that we show throb animations started from
  // CustomButton::StartThrobbing which should start throbbing the button
  // regardless of whether it is hovered.
  if (button->show_multiple_icon_states() &&
      ((state == TextButton::STATE_HOVERED) ||
       (state == TextButton::STATE_PRESSED) ||
       ((state == TextButton::STATE_NORMAL) && animating))) {
    painter = (state == TextButton::STATE_PRESSED) ?
        pushed_painter_.get() : hot_painter_.get();
  }
  if (painter) {
    if (animating) {
      // TODO(pkasting): Really this should crossfade between states so it could
      // handle the case of having a non-NULL |normal_painter_|.
      canvas->SaveLayerAlpha(static_cast<uint8>(
          button->GetAnimation()->CurrentValueBetween(0, 255)));
      painter->Paint(canvas, view.size());
      canvas->Restore();
    } else {
      painter->Paint(canvas, view.size());
    }
  }
}

gfx::Size TextButtonDefaultBorder::GetMinimumSize() const {
  gfx::Size size;
  if (normal_painter_)
    size.SetToMax(normal_painter_->GetMinimumSize());
  if (hot_painter_)
    size.SetToMax(hot_painter_->GetMinimumSize());
  if (pushed_painter_)
    size.SetToMax(pushed_painter_->GetMinimumSize());
  return size;
}


// TextButtonNativeThemeBorder ------------------------------------------------

TextButtonNativeThemeBorder::TextButtonNativeThemeBorder(
    NativeThemeDelegate* delegate)
    : delegate_(delegate) {
  SetInsets(gfx::Insets(kPreferredNativeThemePaddingVertical,
                        kPreferredNativeThemePaddingHorizontal,
                        kPreferredNativeThemePaddingVertical,
                        kPreferredNativeThemePaddingHorizontal));
}

TextButtonNativeThemeBorder::~TextButtonNativeThemeBorder() {
}

void TextButtonNativeThemeBorder::Paint(const View& view, gfx::Canvas* canvas) {
  const ui::NativeTheme* theme = view.GetNativeTheme();
  const TextButtonBase* tb = static_cast<const TextButton*>(&view);
  ui::NativeTheme::Part part = delegate_->GetThemePart();
  gfx::Rect rect(delegate_->GetThemePaintRect());

  if (tb->show_multiple_icon_states() &&
      delegate_->GetThemeAnimation() != NULL &&
      delegate_->GetThemeAnimation()->is_animating()) {
    // Paint background state.
    ui::NativeTheme::ExtraParams prev_extra;
    ui::NativeTheme::State prev_state =
        delegate_->GetBackgroundThemeState(&prev_extra);
    theme->Paint(canvas->sk_canvas(), part, prev_state, rect, prev_extra);

    // Composite foreground state above it.
    ui::NativeTheme::ExtraParams extra;
    ui::NativeTheme::State state = delegate_->GetForegroundThemeState(&extra);
    int alpha = delegate_->GetThemeAnimation()->CurrentValueBetween(0, 255);
    canvas->SaveLayerAlpha(static_cast<uint8>(alpha));
    theme->Paint(canvas->sk_canvas(), part, state, rect, extra);
    canvas->Restore();
  } else {
    ui::NativeTheme::ExtraParams extra;
    ui::NativeTheme::State state = delegate_->GetThemeState(&extra);
    theme->Paint(canvas->sk_canvas(), part, state, rect, extra);
  }
}


// TextButtonBase -------------------------------------------------------------

TextButtonBase::TextButtonBase(ButtonListener* listener, const string16& text)
    : CustomButton(listener),
      alignment_(ALIGN_LEFT),
      font_(ResourceBundle::GetSharedInstance().GetFont(
          ResourceBundle::BaseFont)),
      has_text_shadow_(false),
      active_text_shadow_color_(0),
      inactive_text_shadow_color_(0),
      text_shadow_offset_(gfx::Point(1, 1)),
      min_width_(0),
      min_height_(0),
      max_width_(0),
      show_multiple_icon_states_(true),
      is_default_(false),
      multi_line_(false),
      use_enabled_color_from_theme_(true),
      use_disabled_color_from_theme_(true),
      use_highlight_color_from_theme_(true),
      use_hover_color_from_theme_(true),
      focus_painter_(Painter::CreateDashedFocusPainter()) {
  SetText(text);
  // OnNativeThemeChanged sets the color member variables.
  TextButtonBase::OnNativeThemeChanged(GetNativeTheme());
  SetAnimationDuration(kHoverAnimationDurationMs);
}

TextButtonBase::~TextButtonBase() {
}

void TextButtonBase::SetIsDefault(bool is_default) {
  if (is_default == is_default_)
    return;
  is_default_ = is_default;
  if (is_default_)
    AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
  else
    RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
  SchedulePaint();
}

void TextButtonBase::SetText(const string16& text) {
  if (text == text_)
    return;
  text_ = text;
  SetAccessibleName(text);
  UpdateTextSize();
}

void TextButtonBase::SetFont(const gfx::Font& font) {
  font_ = font;
  UpdateTextSize();
}

void TextButtonBase::SetEnabledColor(SkColor color) {
  color_enabled_ = color;
  use_enabled_color_from_theme_ = false;
  UpdateColor();
}

void TextButtonBase::SetDisabledColor(SkColor color) {
  color_disabled_ = color;
  use_disabled_color_from_theme_ = false;
  UpdateColor();
}

void TextButtonBase::SetHighlightColor(SkColor color) {
  color_highlight_ = color;
  use_highlight_color_from_theme_ = false;
}

void TextButtonBase::SetHoverColor(SkColor color) {
  color_hover_ = color;
  use_hover_color_from_theme_ = false;
}

void TextButtonBase::SetTextShadowColors(SkColor active_color,
                                         SkColor inactive_color) {
  active_text_shadow_color_ = active_color;
  inactive_text_shadow_color_ = inactive_color;
  has_text_shadow_ = true;
}

void TextButtonBase::SetTextShadowOffset(int x, int y) {
  text_shadow_offset_.SetPoint(x, y);
}

void TextButtonBase::ClearEmbellishing() {
  has_text_shadow_ = false;
}

void TextButtonBase::ClearMaxTextSize() {
  max_text_size_ = text_size_;
}

void TextButtonBase::SetShowMultipleIconStates(bool show_multiple_icon_states) {
  show_multiple_icon_states_ = show_multiple_icon_states;
}

void TextButtonBase::SetMultiLine(bool multi_line) {
  if (multi_line != multi_line_) {
    multi_line_ = multi_line;
    max_text_size_.SetSize(0, 0);
    UpdateTextSize();
    SchedulePaint();
  }
}

gfx::Size TextButtonBase::GetPreferredSize() {
  gfx::Insets insets = GetInsets();

  // Use the max size to set the button boundaries.
  // In multiline mode max size can be undefined while
  // width() is 0, so max it out with current text size.
  gfx::Size prefsize(std::max(max_text_size_.width(),
                              text_size_.width()) + insets.width(),
                     std::max(max_text_size_.height(),
                              text_size_.height()) + insets.height());

  if (max_width_ > 0)
    prefsize.set_width(std::min(max_width_, prefsize.width()));

  prefsize.set_width(std::max(prefsize.width(), min_width_));
  prefsize.set_height(std::max(prefsize.height(), min_height_));

  return prefsize;
}

int TextButtonBase::GetHeightForWidth(int w) {
  if (!multi_line_)
    return View::GetHeightForWidth(w);

  if (max_width_ > 0)
    w = std::min(max_width_, w);

  gfx::Size text_size;
  CalculateTextSize(&text_size, w);
  int height = text_size.height() + GetInsets().height();

  return std::max(height, min_height_);
}

void TextButtonBase::OnPaint(gfx::Canvas* canvas) {
  PaintButton(canvas, PB_NORMAL);
}

void TextButtonBase::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  if (multi_line_)
    UpdateTextSize();
}

const gfx::Animation* TextButtonBase::GetAnimation() const {
  return hover_animation_.get();
}

void TextButtonBase::UpdateColor() {
  color_ = enabled() ? color_enabled_ : color_disabled_;
}

void TextButtonBase::UpdateTextSize() {
  int text_width = width();
  // If width is defined, use GetTextBounds.width() for maximum text width,
  // as it will take size of checkbox/radiobutton into account.
  if (text_width != 0) {
    gfx::Rect text_bounds = GetTextBounds();
    text_width = text_bounds.width();
  }
  CalculateTextSize(&text_size_, text_width);
  // Before layout width() is 0, and multiline text will be treated as one line.
  // Do not store max_text_size in this case. UpdateTextSize will be called
  // again once width() changes.
  if (!multi_line_ || text_width != 0) {
    max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()),
                           std::max(max_text_size_.height(),
                                    text_size_.height()));
    PreferredSizeChanged();
  }
}

void TextButtonBase::CalculateTextSize(gfx::Size* text_size, int max_width) {
  int h = font_.GetHeight();
  int w = multi_line_ ? max_width : 0;
  int flags = ComputeCanvasStringFlags();
  if (!multi_line_)
    flags |= gfx::Canvas::NO_ELLIPSIS;

  gfx::Canvas::SizeStringInt(text_, font_, &w, &h, 0, flags);
  text_size->SetSize(w, h);
}

int TextButtonBase::ComputeCanvasStringFlags() const {
  if (!multi_line_)
    return 0;

  int flags = gfx::Canvas::MULTI_LINE;
  switch (alignment_) {
    case ALIGN_LEFT:
      flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_RIGHT:
      flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
      break;
    case ALIGN_CENTER:
      flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
      break;
  }
  return flags;
}

void TextButtonBase::OnFocus() {
  View::OnFocus();
  if (focus_painter_)
    SchedulePaint();
}

void TextButtonBase::OnBlur() {
  View::OnBlur();
  if (focus_painter_)
    SchedulePaint();
}

void TextButtonBase::GetExtraParams(
    ui::NativeTheme::ExtraParams* params) const {
  params->button.checked = false;
  params->button.indeterminate = false;
  params->button.is_default = false;
  params->button.is_focused = false;
  params->button.has_border = false;
  params->button.classic_state = 0;
  params->button.background_color =
      GetNativeTheme()->GetSystemColor(
          ui::NativeTheme::kColorId_ButtonBackgroundColor);
}

gfx::Rect TextButtonBase::GetContentBounds(int extra_width) const {
  gfx::Insets insets = GetInsets();
  int available_width = width() - insets.width();
  int content_width = text_size_.width() + extra_width;
  int content_x = 0;
  switch(alignment_) {
    case ALIGN_LEFT:
      content_x = insets.left();
      break;
    case ALIGN_RIGHT:
      content_x = width() - insets.right() - content_width;
      if (content_x < insets.left())
        content_x = insets.left();
      break;
    case ALIGN_CENTER:
      content_x = insets.left() + std::max(0,
          (available_width - content_width) / 2);
      break;
  }
  content_width = std::min(content_width,
                           width() - insets.right() - content_x);
  int available_height = height() - insets.height();
  int content_y = (available_height - text_size_.height()) / 2 + insets.top();

  gfx::Rect bounds(content_x, content_y, content_width, text_size_.height());
  return bounds;
}

gfx::Rect TextButtonBase::GetTextBounds() const {
  return GetContentBounds(0);
}

void TextButtonBase::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
  focus_painter_ = focus_painter.Pass();
}

void TextButtonBase::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
  if (mode == PB_NORMAL) {
    OnPaintBackground(canvas);
    OnPaintBorder(canvas);
    Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
  }

  gfx::Rect text_bounds(GetTextBounds());
  if (text_bounds.width() > 0) {
    // Because the text button can (at times) draw multiple elements on the
    // canvas, we can not mirror the button by simply flipping the canvas as
    // doing this will mirror the text itself. Flipping the canvas will also
    // make the icons look wrong because icons are almost always represented as
    // direction-insensitive images and such images should never be flipped
    // horizontally.
    //
    // Due to the above, we must perform the flipping manually for RTL UIs.
    text_bounds.set_x(GetMirroredXForRect(text_bounds));

    SkColor text_color = (show_multiple_icon_states_ &&
        (state() == STATE_HOVERED || state() == STATE_PRESSED)) ?
            color_hover_ : color_;

    int draw_string_flags = gfx::Canvas::DefaultCanvasTextAlignment() |
        ComputeCanvasStringFlags();

    if (mode == PB_FOR_DRAG) {
      // Disable sub-pixel rendering as background is transparent.
      draw_string_flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING;

#if defined(OS_WIN)
      // TODO(erg): Either port DrawStringWithHalo to linux or find an
      // alternative here.
      canvas->DrawStringWithHalo(text_, font_, SK_ColorBLACK, SK_ColorWHITE,
          text_bounds.x(), text_bounds.y(), text_bounds.width(),
          text_bounds.height(), draw_string_flags);
#else
      canvas->DrawStringInt(text_,
                            font_,
                            text_color,
                            text_bounds.x(),
                            text_bounds.y(),
                            text_bounds.width(),
                            text_bounds.height(),
                            draw_string_flags);
#endif
    } else {
      gfx::ShadowValues shadows;
      if (has_text_shadow_) {
        SkColor color = GetWidget()->IsActive() ? active_text_shadow_color_ :
                                                  inactive_text_shadow_color_;
        shadows.push_back(gfx::ShadowValue(text_shadow_offset_, 0, color));
      }
      canvas->DrawStringWithShadows(text_, font_, text_color, text_bounds,
                                    0, draw_string_flags, shadows);
    }
  }
}

gfx::Size TextButtonBase::GetMinimumSize() {
  return max_text_size_;
}

void TextButtonBase::OnEnabledChanged() {
  // We should always call UpdateColor() since the state of the button might be
  // changed by other functions like CustomButton::SetState().
  UpdateColor();
  CustomButton::OnEnabledChanged();
}

const char* TextButtonBase::GetClassName() const {
  return kViewClassName;
}

void TextButtonBase::OnNativeThemeChanged(const ui::NativeTheme* theme) {
  if (use_enabled_color_from_theme_) {
    color_enabled_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonEnabledColor);
  }
  if (use_disabled_color_from_theme_) {
    color_disabled_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonDisabledColor);
  }
  if (use_highlight_color_from_theme_) {
    color_highlight_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonHighlightColor);
  }
  if (use_hover_color_from_theme_) {
    color_hover_ = theme->GetSystemColor(
        ui::NativeTheme::kColorId_ButtonHoverColor);
  }
  UpdateColor();
}

gfx::Rect TextButtonBase::GetThemePaintRect() const {
  return GetLocalBounds();
}

ui::NativeTheme::State TextButtonBase::GetThemeState(
    ui::NativeTheme::ExtraParams* params) const {
  GetExtraParams(params);
  switch(state()) {
    case STATE_DISABLED:
      return ui::NativeTheme::kDisabled;
    case STATE_NORMAL:
      return ui::NativeTheme::kNormal;
    case STATE_HOVERED:
      return ui::NativeTheme::kHovered;
    case STATE_PRESSED:
      return ui::NativeTheme::kPressed;
    default:
      NOTREACHED() << "Unknown state: " << state();
      return ui::NativeTheme::kNormal;
  }
}

const gfx::Animation* TextButtonBase::GetThemeAnimation() const {
#if defined(OS_WIN)
  if (GetNativeTheme() == ui::NativeThemeWin::instance()) {
    return ui::NativeThemeWin::instance()->IsThemingActive() ?
        hover_animation_.get() : NULL;
  }
#endif
  return hover_animation_.get();
}

ui::NativeTheme::State TextButtonBase::GetBackgroundThemeState(
  ui::NativeTheme::ExtraParams* params) const {
  GetExtraParams(params);
  return ui::NativeTheme::kNormal;
}

ui::NativeTheme::State TextButtonBase::GetForegroundThemeState(
  ui::NativeTheme::ExtraParams* params) const {
  GetExtraParams(params);
  return ui::NativeTheme::kHovered;
}


// TextButton -----------------------------------------------------------------

TextButton::TextButton(ButtonListener* listener, const string16& text)
    : TextButtonBase(listener, text),
      icon_placement_(ICON_ON_LEFT),
      has_hover_icon_(false),
      has_pushed_icon_(false),
      icon_text_spacing_(kDefaultIconTextSpacing),
      ignore_minimum_size_(true) {
  set_border(new TextButtonDefaultBorder);
  SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets(
                      gfx::Insets(kFocusRectInset, kFocusRectInset,
                                  kFocusRectInset, kFocusRectInset)));
}

TextButton::~TextButton() {
}

void TextButton::SetIcon(const gfx::ImageSkia& icon) {
  icon_ = icon;
  SchedulePaint();
}

void TextButton::SetHoverIcon(const gfx::ImageSkia& icon) {
  icon_hover_ = icon;
  has_hover_icon_ = true;
  SchedulePaint();
}

void TextButton::SetPushedIcon(const gfx::ImageSkia& icon) {
  icon_pushed_ = icon;
  has_pushed_icon_ = true;
  SchedulePaint();
}

gfx::Size TextButton::GetPreferredSize() {
  gfx::Size prefsize(TextButtonBase::GetPreferredSize());
  prefsize.Enlarge(icon_.width(), 0);
  prefsize.set_height(std::max(prefsize.height(), icon_.height()));

  // Use the max size to set the button boundaries.
  if (icon_.width() > 0 && !text_.empty())
    prefsize.Enlarge(icon_text_spacing_, 0);

  if (max_width_ > 0)
    prefsize.set_width(std::min(max_width_, prefsize.width()));

#if defined(OS_WIN)
  // Clamp the size returned to at least the minimum size.
  if (!ignore_minimum_size_) {
    gfx::PlatformFontWin* platform_font =
        static_cast<gfx::PlatformFontWin*>(font_.platform_font());
    prefsize.set_width(std::max(
        prefsize.width(),
        platform_font->horizontal_dlus_to_pixels(kMinWidthDLUs)));
    prefsize.set_height(std::max(
        prefsize.height(),
        platform_font->vertical_dlus_to_pixels(kMinHeightDLUs)));
  }
#endif

  prefsize.set_width(std::max(prefsize.width(), min_width_));
  prefsize.set_height(std::max(prefsize.height(), min_height_));

  return prefsize;
}

void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
  TextButtonBase::PaintButton(canvas, mode);

  const gfx::ImageSkia& icon = GetImageToPaint();

  if (icon.width() > 0) {
    gfx::Rect text_bounds = GetTextBounds();
    int icon_x;
    int spacing = text_.empty() ? 0 : icon_text_spacing_;
    gfx::Insets insets = GetInsets();
    if (icon_placement_ == ICON_ON_LEFT) {
      icon_x = text_bounds.x() - icon.width() - spacing;
    } else if (icon_placement_ == ICON_ON_RIGHT) {
      icon_x = text_bounds.right() + spacing;
    } else {  // ICON_CENTERED
      DCHECK(text_.empty());
      icon_x = (width() - insets.width() - icon.width()) / 2 + insets.left();
    }

    int available_height = height() - insets.height();
    int icon_y = (available_height - icon.height()) / 2 + insets.top();

    // Mirroring the icon position if necessary.
    gfx::Rect icon_bounds(icon_x, icon_y, icon.width(), icon.height());
    icon_bounds.set_x(GetMirroredXForRect(icon_bounds));
    canvas->DrawImageInt(icon, icon_bounds.x(), icon_bounds.y());
  }
}

void TextButton::set_ignore_minimum_size(bool ignore_minimum_size) {
  ignore_minimum_size_ = ignore_minimum_size;
}

const char* TextButton::GetClassName() const {
  return kViewClassName;
}

ui::NativeTheme::Part TextButton::GetThemePart() const {
  return ui::NativeTheme::kPushButton;
}

void TextButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const {
  TextButtonBase::GetExtraParams(params);
  params->button.is_default = is_default_;
}

gfx::Rect TextButton::GetTextBounds() const {
  int extra_width = 0;

  const gfx::ImageSkia& icon = GetImageToPaint();
  if (icon.width() > 0)
    extra_width = icon.width() + (text_.empty() ? 0 : icon_text_spacing_);

  gfx::Rect bounds(GetContentBounds(extra_width));

  if (extra_width > 0) {
    // Make sure the icon is always fully visible.
    if (icon_placement_ == ICON_ON_LEFT) {
      bounds.Inset(extra_width, 0, 0, 0);
    } else if (icon_placement_ == ICON_ON_RIGHT) {
      bounds.Inset(0, 0, extra_width, 0);
    }
  }

  return bounds;
}

const gfx::ImageSkia& TextButton::GetImageToPaint() const {
  if (show_multiple_icon_states_) {
    if (has_hover_icon_ && (state() == STATE_HOVERED))
      return icon_hover_;
    if (has_pushed_icon_ && (state() == STATE_PRESSED))
      return icon_pushed_;
  }
  return icon_;
}

}  // namespace views