1// Copyright (c) 2011 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 "chrome/browser/chromeos/login/screen_lock_view.h"
6
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/chromeos/login/rounded_rect_painter.h"
9#include "chrome/browser/chromeos/login/screen_locker.h"
10#include "chrome/browser/chromeos/login/textfield_with_margin.h"
11#include "chrome/browser/chromeos/login/user_manager.h"
12#include "chrome/browser/chromeos/login/user_view.h"
13#include "chrome/browser/chromeos/login/username_view.h"
14#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
15#include "chrome/browser/chromeos/views/copy_background.h"
16#include "chrome/browser/profiles/profile_manager.h"
17#include "content/common/notification_service.h"
18#include "grit/generated_resources.h"
19#include "grit/theme_resources.h"
20#include "ui/base/l10n/l10n_util.h"
21#include "ui/base/resource/resource_bundle.h"
22#include "views/background.h"
23#include "views/border.h"
24#include "views/controls/image_view.h"
25#include "views/controls/label.h"
26#include "views/controls/textfield/native_textfield_wrapper.h"
27#include "views/controls/textfield/textfield.h"
28#include "views/layout/grid_layout.h"
29
30namespace chromeos {
31
32namespace {
33
34const int kCornerRadius = 5;
35
36// A Textfield for password, which also sets focus to itself
37// when a mouse is clicked on it. This is necessary in screen locker
38// as mouse events are grabbed in the screen locker.
39class PasswordField : public TextfieldWithMargin {
40 public:
41  PasswordField()
42      : TextfieldWithMargin(views::Textfield::STYLE_PASSWORD),
43        context_menu_disabled_(false) {
44    set_text_to_display_when_empty(
45        l10n_util::GetStringUTF16(IDS_LOGIN_POD_EMPTY_PASSWORD_TEXT));
46  }
47
48  // views::View overrides.
49  virtual bool OnMousePressed(const views::MouseEvent& e) {
50    RequestFocus();
51    return false;
52  }
53
54  virtual void ViewHierarchyChanged(bool is_add,
55                                    views::View* parent,
56                                    views::View* child) OVERRIDE {
57    Textfield::ViewHierarchyChanged(is_add, parent, child);
58    // Wiat until native widget is created.
59    if (!context_menu_disabled_ && native_wrapper_) {
60      gfx::NativeView widget = native_wrapper_->GetTestingHandle();
61      if (widget) {
62        context_menu_disabled_ = true;
63        g_signal_connect(widget, "button-press-event",
64                         G_CALLBACK(OnButtonPressEventThunk), this);
65      }
66    }
67  }
68
69  CHROMEGTK_CALLBACK_1(PasswordField, gboolean, OnButtonPressEvent,
70                       GdkEventButton*);
71
72 private:
73  bool context_menu_disabled_;
74
75  DISALLOW_COPY_AND_ASSIGN(PasswordField);
76};
77
78gboolean PasswordField::OnButtonPressEvent(GtkWidget* widget,
79                                           GdkEventButton* event) {
80  // Eat button 2/3 and alt + any button to disable context menu.
81  return event->state & GDK_MOD1_MASK ||
82      event->button == 2 ||
83      event->button == 3;
84}
85
86}  // namespace
87
88using views::GridLayout;
89using login::kBorderSize;
90
91ScreenLockView::ScreenLockView(ScreenLocker* screen_locker)
92    : user_view_(NULL),
93      password_field_(NULL),
94      screen_locker_(screen_locker),
95      main_(NULL),
96      username_(NULL) {
97  DCHECK(screen_locker_);
98}
99
100ScreenLockView::~ScreenLockView() {
101}
102
103gfx::Size ScreenLockView::GetPreferredSize() {
104  return main_->GetPreferredSize();
105}
106
107void ScreenLockView::Layout() {
108  int username_height = login::kSelectedLabelHeight;
109  main_->SetBounds(0, 0, width(), height());
110  username_->SetBounds(
111      kBorderSize,
112      login::kUserImageSize - username_height + kBorderSize,
113      login::kUserImageSize,
114      username_height);
115}
116
117void ScreenLockView::Init() {
118  registrar_.Add(this,
119                 NotificationType::LOGIN_USER_IMAGE_CHANGED,
120                 NotificationService::AllSources());
121
122  user_view_ = new UserView(this,
123                            false,  // is_login
124                            true);  // need_background
125  main_ = new views::View();
126  // Use rounded rect background.
127  views::Painter* painter =
128      CreateWizardPainter(&BorderDefinition::kUserBorder);
129
130  main_->set_background(
131      views::Background::CreateBackgroundPainter(true, painter));
132  main_->set_border(CreateWizardBorder(&BorderDefinition::kUserBorder));
133
134  // Password field.
135  password_field_ = new PasswordField();
136
137  password_field_->SetController(this);
138  password_field_->set_background(new CopyBackground(main_));
139
140  // Setup ThrobberView's host view.
141  set_host_view(password_field_);
142
143  // User icon.
144  UserManager::User user = screen_locker_->user();
145  user_view_->SetImage(user.image(), user.image());
146
147  // User name.
148  std::wstring text = UTF8ToWide(user.GetDisplayName());
149
150  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
151  const gfx::Font& font = rb.GetFont(ResourceBundle::MediumBoldFont).DeriveFont(
152      kSelectedUsernameFontDelta);
153
154  // Layouts image, textfield and button components.
155  GridLayout* layout = new GridLayout(main_);
156  main_->SetLayoutManager(layout);
157  views::ColumnSet* column_set = layout->AddColumnSet(0);
158  column_set->AddPaddingColumn(0, kBorderSize);
159  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
160                        GridLayout::USE_PREF, 0, 0);
161  column_set->AddPaddingColumn(0, kBorderSize);
162
163  column_set = layout->AddColumnSet(1);
164  column_set->AddPaddingColumn(0, kBorderSize);
165  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
166                        GridLayout::USE_PREF, 0, 0);
167  column_set->AddPaddingColumn(0, kBorderSize);
168
169  layout->AddPaddingRow(0, kBorderSize);
170  layout->StartRow(0, 0);
171  layout->AddView(user_view_);
172  layout->AddPaddingRow(0, kBorderSize);
173  layout->StartRow(0, 1);
174  layout->AddView(password_field_);
175  layout->AddPaddingRow(0, kBorderSize);
176
177  AddChildView(main_);
178
179  UsernameView* username = UsernameView::CreateShapedUsernameView(text, false);
180  username_ = username;
181  username->SetColor(login::kTextColor);
182  username->SetFont(font);
183  AddChildView(username);
184}
185
186void ScreenLockView::ClearAndSetFocusToPassword() {
187  password_field_->RequestFocus();
188  password_field_->SetText(string16());
189}
190
191void ScreenLockView::SetSignoutEnabled(bool enabled) {
192  user_view_->SetSignoutEnabled(enabled);
193}
194
195gfx::Rect ScreenLockView::GetPasswordBoundsRelativeTo(const views::View* view) {
196  gfx::Point p;
197  views::View::ConvertPointToView(password_field_, view, &p);
198  return gfx::Rect(p, size());
199}
200
201
202void ScreenLockView::SetEnabled(bool enabled) {
203  views::View::SetEnabled(enabled);
204
205  if (!enabled) {
206    // TODO(oshima): Re-enabling does not move the focus to the view
207    // that had a focus (issue http://crbug.com/43131).
208    // Clear focus on the textfield so that re-enabling can set focus
209    // back to the text field.
210    // FocusManager may be null if the view does not have
211    // associated Widget yet.
212    if (password_field_->GetFocusManager())
213      password_field_->GetFocusManager()->ClearFocus();
214  }
215  password_field_->SetEnabled(enabled);
216}
217
218void ScreenLockView::OnSignout() {
219  screen_locker_->Signout();
220}
221
222void ScreenLockView::ContentsChanged(views::Textfield* sender,
223                                     const string16& new_contents) {
224  if (!new_contents.empty())
225    screen_locker_->ClearErrors();
226}
227
228bool ScreenLockView::HandleKeyEvent(
229    views::Textfield* sender,
230    const views::KeyEvent& key_event) {
231  screen_locker_->ClearErrors();
232  if (key_event.key_code() == ui::VKEY_RETURN) {
233    screen_locker_->Authenticate(password_field_->text());
234    return true;
235  }
236  return false;
237}
238
239void ScreenLockView::Observe(
240    NotificationType type,
241    const NotificationSource& source,
242    const NotificationDetails& details) {
243  if (type != NotificationType::LOGIN_USER_IMAGE_CHANGED || !user_view_)
244    return;
245
246  UserManager::User* user = Details<UserManager::User>(details).ptr();
247  if (screen_locker_->user().email() != user->email())
248    return;
249  user_view_->SetImage(user->image(), user->image());
250}
251
252}  // namespace chromeos
253