1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/controls/link.h"
6
7#include "build/build_config.h"
8
9#include "base/logging.h"
10#include "base/strings/utf_string_conversions.h"
11#include "ui/base/accessibility/accessible_view_state.h"
12#include "ui/events/event.h"
13#include "ui/events/keycodes/keyboard_codes.h"
14#include "ui/gfx/canvas.h"
15#include "ui/gfx/color_utils.h"
16#include "ui/gfx/font.h"
17#include "ui/views/controls/link_listener.h"
18
19#if defined(USE_AURA)
20#include "ui/base/cursor/cursor.h"
21#endif
22
23namespace views {
24
25const char Link::kViewClassName[] = "Link";
26
27Link::Link() : Label(string16()) {
28  Init();
29}
30
31Link::Link(const string16& title) : Label(title) {
32  Init();
33}
34
35Link::~Link() {
36}
37
38SkColor Link::GetDefaultEnabledColor() {
39#if defined(OS_WIN)
40  return color_utils::GetSysSkColor(COLOR_HOTLIGHT);
41#else
42  return SkColorSetRGB(0, 51, 153);
43#endif
44}
45
46void Link::OnEnabledChanged() {
47  RecalculateFont();
48  View::OnEnabledChanged();
49}
50
51const char* Link::GetClassName() const {
52  return kViewClassName;
53}
54
55gfx::NativeCursor Link::GetCursor(const ui::MouseEvent& event) {
56  if (!enabled())
57    return gfx::kNullCursor;
58#if defined(USE_AURA)
59  return ui::kCursorHand;
60#elif defined(OS_WIN)
61  static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND);
62  return g_hand_cursor;
63#endif
64}
65
66void Link::OnPaint(gfx::Canvas* canvas) {
67  Label::OnPaint(canvas);
68
69  if (HasFocus())
70    canvas->DrawFocusRect(GetLocalBounds());
71}
72
73void Link::OnFocus() {
74  Label::OnFocus();
75  // We render differently focused.
76  SchedulePaint();
77}
78
79void Link::OnBlur() {
80  Label::OnBlur();
81  // We render differently focused.
82  SchedulePaint();
83}
84
85bool Link::HitTestRect(const gfx::Rect& rect) const {
86  // We need to allow clicks on the link. So we override the implementation in
87  // Label and use the default implementation of View.
88  return View::HitTestRect(rect);
89}
90
91bool Link::OnMousePressed(const ui::MouseEvent& event) {
92  if (!enabled() ||
93      (!event.IsLeftMouseButton() && !event.IsMiddleMouseButton()))
94    return false;
95  SetPressed(true);
96  return true;
97}
98
99bool Link::OnMouseDragged(const ui::MouseEvent& event) {
100  SetPressed(enabled() &&
101             (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) &&
102             HitTestPoint(event.location()));
103  return true;
104}
105
106void Link::OnMouseReleased(const ui::MouseEvent& event) {
107  // Change the highlight first just in case this instance is deleted
108  // while calling the controller
109  OnMouseCaptureLost();
110  if (enabled() &&
111      (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) &&
112      HitTestPoint(event.location())) {
113    // Focus the link on click.
114    RequestFocus();
115
116    if (listener_)
117      listener_->LinkClicked(this, event.flags());
118  }
119}
120
121void Link::OnMouseCaptureLost() {
122  SetPressed(false);
123}
124
125bool Link::OnKeyPressed(const ui::KeyEvent& event) {
126  bool activate = ((event.key_code() == ui::VKEY_SPACE) ||
127                   (event.key_code() == ui::VKEY_RETURN));
128  if (!activate)
129    return false;
130
131  SetPressed(false);
132
133  // Focus the link on key pressed.
134  RequestFocus();
135
136  if (listener_)
137    listener_->LinkClicked(this, event.flags());
138
139  return true;
140}
141
142bool Link::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
143  // Make sure we don't process space or enter as accelerators.
144  return (event.key_code() == ui::VKEY_SPACE) ||
145      (event.key_code() == ui::VKEY_RETURN);
146}
147
148void Link::GetAccessibleState(ui::AccessibleViewState* state) {
149  Label::GetAccessibleState(state);
150  state->role = ui::AccessibilityTypes::ROLE_LINK;
151}
152
153void Link::OnGestureEvent(ui::GestureEvent* event) {
154  if (!enabled())
155    return;
156
157  if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
158    SetPressed(true);
159  } else if (event->type() == ui::ET_GESTURE_TAP) {
160    RequestFocus();
161    if (listener_)
162      listener_->LinkClicked(this, event->flags());
163  } else {
164    SetPressed(false);
165    return;
166  }
167  event->SetHandled();
168}
169
170void Link::SetFont(const gfx::Font& font) {
171  Label::SetFont(font);
172  RecalculateFont();
173}
174
175void Link::SetEnabledColor(SkColor color) {
176  requested_enabled_color_ = color;
177  if (!pressed_)
178    Label::SetEnabledColor(requested_enabled_color_);
179}
180
181void Link::SetPressedColor(SkColor color) {
182  requested_pressed_color_ = color;
183  if (pressed_)
184    Label::SetEnabledColor(requested_pressed_color_);
185}
186
187void Link::SetUnderline(bool underline) {
188  if (underline_ == underline)
189    return;
190  underline_ = underline;
191  RecalculateFont();
192}
193
194void Link::Init() {
195  listener_ = NULL;
196  pressed_ = false;
197  underline_ = true;
198  SetEnabledColor(GetDefaultEnabledColor());
199#if defined(OS_WIN)
200  SetDisabledColor(color_utils::GetSysSkColor(COLOR_WINDOWTEXT));
201  SetPressedColor(SkColorSetRGB(200, 0, 0));
202#else
203  // TODO(beng): source from theme provider.
204  SetDisabledColor(SK_ColorBLACK);
205  SetPressedColor(SK_ColorRED);
206#endif
207  RecalculateFont();
208  SetFocusable(true);
209}
210
211void Link::SetPressed(bool pressed) {
212  if (pressed_ != pressed) {
213    pressed_ = pressed;
214    Label::SetEnabledColor(pressed_ ?
215        requested_pressed_color_ : requested_enabled_color_);
216    RecalculateFont();
217    SchedulePaint();
218  }
219}
220
221void Link::RecalculateFont() {
222  // Underline the link iff it is enabled and |underline_| is true.
223  const int style = font().GetStyle();
224  const int intended_style = (enabled() && underline_) ?
225      (style | gfx::Font::UNDERLINE) : (style & ~gfx::Font::UNDERLINE);
226  if (style != intended_style)
227    Label::SetFont(font().DeriveFont(0, intended_style));
228}
229
230}  // namespace views
231