• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/user_controller.h"
6 
7 #include <algorithm>
8 #include <vector>
9 
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/chromeos/login/existing_user_view.h"
12 #include "chrome/browser/chromeos/login/guest_user_view.h"
13 #include "chrome/browser/chromeos/login/helper.h"
14 #include "chrome/browser/chromeos/login/rounded_rect_painter.h"
15 #include "chrome/browser/chromeos/login/user_view.h"
16 #include "chrome/browser/chromeos/login/username_view.h"
17 #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
18 #include "chrome/browser/chromeos/login/wizard_controller.h"
19 #include "chrome/browser/chromeos/user_cros_settings_provider.h"
20 #include "grit/generated_resources.h"
21 #include "grit/theme_resources.h"
22 #include "third_party/cros/chromeos_wm_ipc_enums.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/canvas.h"
26 #include "views/background.h"
27 #include "views/controls/button/native_button.h"
28 #include "views/controls/label.h"
29 #include "views/controls/throbber.h"
30 #include "views/focus/focus_manager.h"
31 #include "views/painter.h"
32 #include "views/screen.h"
33 #include "views/widget/root_view.h"
34 #include "views/widget/widget_gtk.h"
35 
36 using views::Widget;
37 using views::WidgetGtk;
38 
39 namespace chromeos {
40 
41 namespace {
42 
43 // Gap between the border around the image/buttons and user name.
44 const int kUserNameGap = 4;
45 
46 // Approximate height of controls window, this constant is used in new user
47 // case to make border window size close to existing users.
48 #if defined(CROS_FONTS_USING_BCI)
49 const int kControlsHeight = 31;
50 #else
51 const int kControlsHeight = 28;
52 #endif
53 
54 // Vertical interval between the image and the textfield.
55 const int kVerticalIntervalSize = 10;
56 
57 // A window for controls that sets focus to the view when
58 // it first got focus.
59 class ControlsWindow : public WidgetGtk {
60  public:
ControlsWindow(views::View * initial_focus_view)61   explicit ControlsWindow(views::View* initial_focus_view)
62       : WidgetGtk(WidgetGtk::TYPE_WINDOW),
63         initial_focus_view_(initial_focus_view) {
64   }
65 
66  private:
67   // WidgetGtk overrides:
SetInitialFocus()68   virtual void SetInitialFocus() OVERRIDE {
69     if (initial_focus_view_)
70       initial_focus_view_->RequestFocus();
71   }
72 
OnMap(GtkWidget * widget)73   virtual void OnMap(GtkWidget* widget) OVERRIDE {
74     // For some reason, Controls window never gets first expose event,
75     // which makes WM believe that the login screen is not ready.
76     // This is a workaround to let WM show the login screen. While
77     // this may allow WM to show unpainted window, we haven't seen any
78     // issue (yet). We will not investigate this further because we're
79     // migrating to different implemention (WebUI).
80     UpdateFreezeUpdatesProperty(GTK_WINDOW(GetNativeView()),
81                                 false /* remove */);
82   }
83 
84   views::View* initial_focus_view_;
85 
86   DISALLOW_COPY_AND_ASSIGN(ControlsWindow);
87 };
88 
89 // Widget that notifies window manager about clicking on itself.
90 // Doesn't send anything if user is selected.
91 class ClickNotifyingWidget : public views::WidgetGtk {
92  public:
ClickNotifyingWidget(views::WidgetGtk::Type type,UserController * controller)93   ClickNotifyingWidget(views::WidgetGtk::Type type,
94                        UserController* controller)
95       : WidgetGtk(type),
96         controller_(controller) {
97   }
98 
99  private:
OnButtonPress(GtkWidget * widget,GdkEventButton * event)100   gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) {
101     if (!controller_->IsUserSelected())
102       controller_->SelectUserRelative(0);
103 
104     return views::WidgetGtk::OnButtonPress(widget, event);
105   }
106 
107   UserController* controller_;
108 
109   DISALLOW_COPY_AND_ASSIGN(ClickNotifyingWidget);
110 };
111 
CloseWindow(views::Widget * window)112 void CloseWindow(views::Widget* window) {
113   if (!window)
114     return;
115   window->set_widget_delegate(NULL);
116   window->Close();
117 }
118 
119 }  // namespace
120 
121 using login::kBorderSize;
122 using login::kUserImageSize;
123 
124 // static
125 const int UserController::kPadding = 30;
126 
127 // Max size needed when an entry is not selected.
128 const int UserController::kUnselectedSize = 100;
129 const int UserController::kNewUserUnselectedSize = 42;
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 // UserController, public:
133 
UserController(Delegate * delegate,bool is_guest)134 UserController::UserController(Delegate* delegate, bool is_guest)
135     : user_index_(-1),
136       is_user_selected_(false),
137       is_new_user_(!is_guest),
138       is_guest_(is_guest),
139       is_owner_(false),
140       show_name_tooltip_(false),
141       delegate_(delegate),
142       controls_window_(NULL),
143       image_window_(NULL),
144       border_window_(NULL),
145       label_window_(NULL),
146       unselected_label_window_(NULL),
147       user_view_(NULL),
148       label_view_(NULL),
149       unselected_label_view_(NULL),
150       user_input_(NULL),
151       throbber_host_(NULL) {
152 }
153 
UserController(Delegate * delegate,const UserManager::User & user)154 UserController::UserController(Delegate* delegate,
155                                const UserManager::User& user)
156     : user_index_(-1),
157       is_user_selected_(false),
158       is_new_user_(false),
159       is_guest_(false),
160       // Empty 'cached_owner()' means that owner hasn't been cached yet, not
161       // that owner has an empty email.
162       is_owner_(user.email() == UserCrosSettingsProvider::cached_owner()),
163       show_name_tooltip_(false),
164       user_(user),
165       delegate_(delegate),
166       controls_window_(NULL),
167       image_window_(NULL),
168       border_window_(NULL),
169       label_window_(NULL),
170       unselected_label_window_(NULL),
171       user_view_(NULL),
172       label_view_(NULL),
173       unselected_label_view_(NULL),
174       user_input_(NULL),
175       throbber_host_(NULL) {
176   DCHECK(!user.email().empty());
177 }
178 
~UserController()179 UserController::~UserController() {
180   // Reset the widget delegate of every window to NULL, so the user
181   // controller will not get notified about the active window change.
182   // See also crosbug.com/7400.
183   CloseWindow(controls_window_);
184   CloseWindow(image_window_);
185   CloseWindow(border_window_);
186   CloseWindow(label_window_);
187   CloseWindow(unselected_label_window_);
188 }
189 
Init(int index,int total_user_count,bool need_browse_without_signin)190 void UserController::Init(int index,
191                           int total_user_count,
192                           bool need_browse_without_signin) {
193   int controls_height = 0;
194   int controls_width = 0;
195   controls_window_ =
196       CreateControlsWindow(index, &controls_width, &controls_height,
197                            need_browse_without_signin);
198   image_window_ = CreateImageWindow(index);
199   CreateBorderWindow(index, total_user_count, controls_width, controls_height);
200   label_window_ = CreateLabelWindow(index, WM_IPC_WINDOW_LOGIN_LABEL);
201   unselected_label_window_ =
202       CreateLabelWindow(index, WM_IPC_WINDOW_LOGIN_UNSELECTED_LABEL);
203 }
204 
ClearAndEnableFields()205 void UserController::ClearAndEnableFields() {
206   user_input_->EnableInputControls(true);
207   user_input_->ClearAndFocusControls();
208   StopThrobber();
209 }
210 
ClearAndEnablePassword()211 void UserController::ClearAndEnablePassword() {
212   // Somehow focus manager thinks that textfield is still focused but the
213   // textfield doesn't know that. So we clear focus for focus manager so it
214   // sets focus on the textfield again.
215   // TODO(avayvod): Fix the actual issue.
216   views::FocusManager* focus_manager = controls_window_->GetFocusManager();
217   if (focus_manager)
218     focus_manager->ClearFocus();
219   user_input_->EnableInputControls(true);
220   user_input_->ClearAndFocusPassword();
221   StopThrobber();
222 }
223 
EnableNameTooltip(bool enable)224 void UserController::EnableNameTooltip(bool enable) {
225   name_tooltip_enabled_ = enable;
226   std::wstring tooltip_text;
227   if (enable)
228     tooltip_text = GetNameTooltip();
229 
230   if (user_view_)
231     user_view_->SetTooltipText(tooltip_text);
232   if (label_view_)
233     label_view_->SetTooltipText(tooltip_text);
234   if (unselected_label_view_)
235     unselected_label_view_->SetTooltipText(tooltip_text);
236 }
237 
GetMainInputScreenBounds() const238 gfx::Rect UserController::GetMainInputScreenBounds() const {
239   return user_input_->GetMainInputScreenBounds();
240 }
241 
OnUserImageChanged(UserManager::User * user)242 void UserController::OnUserImageChanged(UserManager::User* user) {
243   if (user_.email() != user->email())
244     return;
245   user_.set_image(user->image());
246   // Controller might exist without windows,
247   // i.e. if user pod doesn't fit on the screen.
248   if (user_view_)
249     user_view_->SetImage(user_.image(), user_.image());
250 }
251 
SelectUserRelative(int shift)252 void UserController::SelectUserRelative(int shift) {
253   delegate_->SelectUser(user_index() + shift);
254 }
255 
StartThrobber()256 void UserController::StartThrobber() {
257   throbber_host_->StartThrobber();
258 }
259 
StopThrobber()260 void UserController::StopThrobber() {
261   throbber_host_->StopThrobber();
262 }
263 
UpdateUserCount(int index,int total_user_count)264 void UserController::UpdateUserCount(int index, int total_user_count) {
265   user_index_ = index;
266   std::vector<int> params;
267   params.push_back(index);
268   params.push_back(total_user_count);
269   params.push_back(is_new_user_ ? kNewUserUnselectedSize : kUnselectedSize);
270   params.push_back(kPadding);
271   WmIpc::instance()->SetWindowType(
272       border_window_->GetNativeView(),
273       WM_IPC_WINDOW_LOGIN_BORDER,
274       &params);
275 }
276 
GetAccessibleUserLabel()277 std::string UserController::GetAccessibleUserLabel() {
278   if (is_new_user_)
279     return l10n_util::GetStringUTF8(IDS_ADD_USER);
280   if (is_guest_)
281     return l10n_util::GetStringUTF8(IDS_GUEST);
282   return user_.email();
283 }
284 
285 ////////////////////////////////////////////////////////////////////////////////
286 // UserController, WidgetDelegate implementation:
287 //
OnWidgetActivated(bool active)288 void UserController::OnWidgetActivated(bool active) {
289   is_user_selected_ = active;
290   if (active) {
291     delegate_->OnUserSelected(this);
292     user_view_->SetRemoveButtonVisible(
293         !is_new_user_ && !is_guest_ && !is_owner_);
294   } else {
295     user_view_->SetRemoveButtonVisible(false);
296     delegate_->ClearErrors();
297   }
298 }
299 
300 ////////////////////////////////////////////////////////////////////////////////
301 // UserController, NewUserView::Delegate implementation:
302 //
OnLogin(const std::string & username,const std::string & password)303 void UserController::OnLogin(const std::string& username,
304                              const std::string& password) {
305   if (is_new_user_)
306     user_.set_email(username);
307 
308   user_input_->EnableInputControls(false);
309   StartThrobber();
310 
311   delegate_->Login(this, UTF8ToUTF16(password));
312 }
313 
OnCreateAccount()314 void UserController::OnCreateAccount() {
315   user_input_->EnableInputControls(false);
316   StartThrobber();
317 
318   delegate_->CreateAccount();
319 }
320 
OnStartEnterpriseEnrollment()321 void UserController::OnStartEnterpriseEnrollment() {
322   delegate_->StartEnterpriseEnrollment();
323 }
324 
OnLoginAsGuest()325 void UserController::OnLoginAsGuest() {
326   user_input_->EnableInputControls(false);
327   StartThrobber();
328 
329   delegate_->LoginAsGuest();
330 }
331 
ClearErrors()332 void UserController::ClearErrors() {
333   delegate_->ClearErrors();
334 }
335 
NavigateAway()336 void UserController::NavigateAway() {
337   SelectUserRelative(-1);
338 }
339 
340 ////////////////////////////////////////////////////////////////////////////////
341 // UserController, UserView::Delegate implementation:
342 //
OnLocaleChanged()343 void UserController::OnLocaleChanged() {
344   // Update text tooltips on guest and new user pods.
345   if (is_guest_ || is_new_user_) {
346     if (name_tooltip_enabled_)
347       EnableNameTooltip(name_tooltip_enabled_);
348   }
349   label_view_->SetFont(GetLabelFont());
350   unselected_label_view_->SetFont(GetUnselectedLabelFont());
351 }
352 
OnRemoveUser()353 void UserController::OnRemoveUser() {
354   delegate_->RemoveUser(this);
355 }
356 
357 ////////////////////////////////////////////////////////////////////////////////
358 // UserController, private:
359 //
ConfigureLoginWindow(WidgetGtk * window,int index,const gfx::Rect & bounds,chromeos::WmIpcWindowType type,views::View * contents_view)360 void UserController::ConfigureLoginWindow(WidgetGtk* window,
361                                           int index,
362                                           const gfx::Rect& bounds,
363                                           chromeos::WmIpcWindowType type,
364                                           views::View* contents_view) {
365   window->MakeTransparent();
366   window->Init(NULL, bounds);
367   window->SetContentsView(contents_view);
368   window->set_widget_delegate(this);
369 
370   std::vector<int> params;
371   params.push_back(index);
372   WmIpc::instance()->SetWindowType(
373       window->GetNativeView(),
374       type,
375       &params);
376 
377   GdkWindow* gdk_window = window->GetNativeView()->window;
378   gdk_window_set_back_pixmap(gdk_window, NULL, false);
379 
380   window->Show();
381 }
382 
CreateControlsWindow(int index,int * width,int * height,bool need_browse_without_signin)383 WidgetGtk* UserController::CreateControlsWindow(
384     int index,
385     int* width, int* height,
386     bool need_browse_without_signin) {
387   views::View* control_view;
388   if (is_new_user_) {
389     NewUserView* new_user_view =
390         new NewUserView(this, true, need_browse_without_signin);
391     new_user_view->Init();
392     control_view = new_user_view;
393     user_input_ = new_user_view;
394     throbber_host_ = new_user_view;
395   } else if (is_guest_) {
396     GuestUserView* guest_user_view = new GuestUserView(this);
397     guest_user_view->RecreateFields();
398     control_view = guest_user_view;
399     user_input_ = guest_user_view;
400     throbber_host_ = guest_user_view;
401   } else {
402     ExistingUserView* existing_user_view = new ExistingUserView(this);
403     existing_user_view->RecreateFields();
404     control_view = existing_user_view;
405     user_input_ = existing_user_view;
406     throbber_host_ = existing_user_view;
407   }
408 
409   *height = kControlsHeight;
410   *width = kUserImageSize;
411   if (is_new_user_) {
412     gfx::Size size = control_view->GetPreferredSize();
413     *width = size.width();
414     *height = size.height();
415   }
416 
417   WidgetGtk* window = new ControlsWindow(control_view);
418   ConfigureLoginWindow(window,
419                        index,
420                        gfx::Rect(*width, *height),
421                        WM_IPC_WINDOW_LOGIN_CONTROLS,
422                        control_view);
423   return window;
424 }
425 
CreateImageWindow(int index)426 WidgetGtk* UserController::CreateImageWindow(int index) {
427   user_view_ = new UserView(this, true, !is_new_user_);
428 
429   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
430   if (is_guest_) {
431     SkBitmap* image = rb.GetBitmapNamed(IDR_LOGIN_GUEST);
432     user_view_->SetImage(*image, *image);
433   } else if (is_new_user_) {
434     SkBitmap* image = rb.GetBitmapNamed(IDR_LOGIN_ADD_USER);
435     SkBitmap* image_hover = rb.GetBitmapNamed(IDR_LOGIN_ADD_USER_HOVER);
436     user_view_->SetImage(*image, *image_hover);
437   } else {
438     user_view_->SetImage(user_.image(), user_.image());
439   }
440 
441   WidgetGtk* window = new ClickNotifyingWidget(WidgetGtk::TYPE_WINDOW, this);
442   ConfigureLoginWindow(window,
443                        index,
444                        gfx::Rect(user_view_->GetPreferredSize()),
445                        WM_IPC_WINDOW_LOGIN_IMAGE,
446                        user_view_);
447 
448   return window;
449 }
450 
CreateBorderWindow(int index,int total_user_count,int controls_width,int controls_height)451 void UserController::CreateBorderWindow(int index,
452                                         int total_user_count,
453                                         int controls_width,
454                                         int controls_height) {
455   // New user login controls window is much higher than existing user's controls
456   // window so window manager will place the control instead of image window.
457   // New user will have 0 size border.
458   int width = controls_width;
459   int height = controls_height;
460   if (!is_new_user_) {
461     width += kBorderSize * 2;
462     height += 2 * kBorderSize + kUserImageSize + kVerticalIntervalSize;
463   }
464 
465   Widget::CreateParams params(Widget::CreateParams::TYPE_WINDOW);
466   params.transparent = true;
467   border_window_ = Widget::CreateWidget(params);
468   border_window_->Init(NULL, gfx::Rect(0, 0, width, height));
469   if (!is_new_user_) {
470     views::View* background_view = new views::View();
471     views::Painter* painter = CreateWizardPainter(
472         &BorderDefinition::kUserBorder);
473     background_view->set_background(
474         views::Background::CreateBackgroundPainter(true, painter));
475     border_window_->SetContentsView(background_view);
476   }
477   UpdateUserCount(index, total_user_count);
478 
479   GdkWindow* gdk_window = border_window_->GetNativeView()->window;
480   gdk_window_set_back_pixmap(gdk_window, NULL, false);
481 
482   border_window_->Show();
483 }
484 
CreateLabelWindow(int index,WmIpcWindowType type)485 WidgetGtk* UserController::CreateLabelWindow(int index,
486                                              WmIpcWindowType type) {
487   std::wstring text;
488   if (is_guest_) {
489     text = std::wstring();
490   } else if (is_new_user_) {
491     // Add user should have label only in activated state.
492     // When new user is the only, label is not needed.
493     if (type == WM_IPC_WINDOW_LOGIN_LABEL && index != 0)
494       text = UTF16ToWide(l10n_util::GetStringUTF16(IDS_ADD_USER));
495   } else {
496     text = UTF8ToWide(user_.GetDisplayName());
497   }
498 
499   views::Label* label = NULL;
500 
501   if (is_new_user_) {
502     label = new views::Label(text);
503   } else if (type == WM_IPC_WINDOW_LOGIN_LABEL) {
504     label = UsernameView::CreateShapedUsernameView(text, false);
505   } else {
506     DCHECK(type == WM_IPC_WINDOW_LOGIN_UNSELECTED_LABEL);
507     // TODO(altimofeev): switch to the rounded username view.
508     label = UsernameView::CreateShapedUsernameView(text, true);
509   }
510 
511   const gfx::Font& font = (type == WM_IPC_WINDOW_LOGIN_LABEL) ?
512       GetLabelFont() : GetUnselectedLabelFont();
513   label->SetFont(font);
514   label->SetColor(login::kTextColor);
515 
516   if (type == WM_IPC_WINDOW_LOGIN_LABEL)
517     label_view_ = label;
518   else
519     unselected_label_view_ = label;
520 
521   int width = (type == WM_IPC_WINDOW_LOGIN_LABEL) ?
522       kUserImageSize : kUnselectedSize;
523   if (is_new_user_) {
524     // Make label as small as possible to don't show tooltip.
525     width = 0;
526   }
527   int height = (type == WM_IPC_WINDOW_LOGIN_LABEL) ?
528       login::kSelectedLabelHeight : login::kUnselectedLabelHeight;
529   WidgetGtk* window = new ClickNotifyingWidget(WidgetGtk::TYPE_WINDOW, this);
530   ConfigureLoginWindow(window,
531                        index,
532                        gfx::Rect(0, 0, width, height),
533                        type,
534                        label);
535   return window;
536 }
537 
GetLabelFont()538 gfx::Font UserController::GetLabelFont() {
539   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
540   return rb.GetFont(ResourceBundle::MediumBoldFont).DeriveFont(
541       kSelectedUsernameFontDelta);
542 }
543 
GetUnselectedLabelFont()544 gfx::Font UserController::GetUnselectedLabelFont() {
545   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
546   return rb.GetFont(ResourceBundle::BaseFont).DeriveFont(
547       kUnselectedUsernameFontDelta, gfx::Font::BOLD);
548 }
549 
550 
GetNameTooltip() const551 std::wstring UserController::GetNameTooltip() const {
552   if (is_new_user_)
553     return UTF16ToWide(l10n_util::GetStringUTF16(IDS_ADD_USER));
554   else if (is_guest_)
555     return UTF16ToWide(l10n_util::GetStringUTF16(IDS_GO_INCOGNITO_BUTTON));
556   else
557     return UTF8ToWide(user_.GetNameTooltip());
558 }
559 
560 }  // namespace chromeos
561