Home | History | Annotate | Download | only in profiles
      1 // Copyright 2014 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/ui/views/profiles/new_avatar_button.h"
      6 
      7 #include "base/win/windows_version.h"
      8 #include "chrome/browser/browser_process.h"
      9 #include "chrome/browser/profiles/profile_manager.h"
     10 #include "chrome/browser/profiles/profiles_state.h"
     11 #include "chrome/browser/ui/browser.h"
     12 #include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
     13 #include "grit/theme_resources.h"
     14 #include "ui/base/resource/resource_bundle.h"
     15 #include "ui/gfx/canvas.h"
     16 #include "ui/views/border.h"
     17 #include "ui/views/controls/button/label_button_border.h"
     18 #include "ui/views/painter.h"
     19 
     20 namespace {
     21 
     22 scoped_ptr<views::Border> CreateBorder(const int normal_image_set[],
     23                                        const int hot_image_set[],
     24                                        const int pushed_image_set[]) {
     25   scoped_ptr<views::LabelButtonBorder> border(
     26       new views::LabelButtonBorder(views::Button::STYLE_TEXTBUTTON));
     27   border->SetPainter(false, views::Button::STATE_NORMAL,
     28       views::Painter::CreateImageGridPainter(normal_image_set));
     29   border->SetPainter(false, views::Button::STATE_HOVERED,
     30       views::Painter::CreateImageGridPainter(hot_image_set));
     31   border->SetPainter(false, views::Button::STATE_PRESSED,
     32       views::Painter::CreateImageGridPainter(pushed_image_set));
     33 
     34   const int kLeftRightInset = 8;
     35   const int kTopInset = 2;
     36   const int kBottomInset = 4;
     37   border->set_insets(gfx::Insets(kTopInset, kLeftRightInset,
     38                                  kBottomInset, kLeftRightInset));
     39 
     40   return border.PassAs<views::Border>();
     41 }
     42 
     43 }  // namespace
     44 
     45 NewAvatarButton::NewAvatarButton(views::ButtonListener* listener,
     46                                  AvatarButtonStyle button_style,
     47                                  Browser* browser)
     48     : LabelButton(listener, base::string16()),
     49       browser_(browser),
     50       has_auth_error_(false),
     51       suppress_mouse_released_action_(false) {
     52   set_animate_on_state_change(false);
     53   SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE);
     54   SetTextColor(views::Button::STATE_HOVERED, SK_ColorWHITE);
     55   SetTextColor(views::Button::STATE_PRESSED, SK_ColorWHITE);
     56   SetTextShadows(gfx::ShadowValues(10,
     57       gfx::ShadowValue(gfx::Point(), 1.0f, SK_ColorDKGRAY)));
     58   SetTextSubpixelRenderingEnabled(false);
     59   SetHorizontalAlignment(gfx::ALIGN_CENTER);
     60 
     61   // The largest text height that fits in the button. If the font list height
     62   // is larger than this, it will be shrunk to match it.
     63   // TODO(noms): Calculate this constant algorithmically.
     64   const int kDisplayFontHeight = 15;
     65   SetFontList(GetFontList().DeriveWithHeightUpperBound(kDisplayFontHeight));
     66 
     67   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
     68   if (button_style == THEMED_BUTTON) {
     69     const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_NORMAL);
     70     const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_HOVER);
     71     const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_PRESSED);
     72 
     73     SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
     74     generic_avatar_ =
     75         *rb->GetImageNamed(IDR_AVATAR_THEMED_BUTTON_AVATAR).ToImageSkia();
     76 #if defined(OS_WIN)
     77   } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
     78     const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_NORMAL);
     79     const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_HOVER);
     80     const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_PRESSED);
     81 
     82     SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
     83     generic_avatar_ =
     84         *rb->GetImageNamed(IDR_AVATAR_METRO_BUTTON_AVATAR).ToImageSkia();
     85 #endif
     86   } else {
     87     const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_NORMAL);
     88     const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_HOVER);
     89     const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_PRESSED);
     90 
     91     SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
     92     generic_avatar_ =
     93         *rb->GetImageNamed(IDR_AVATAR_GLASS_BUTTON_AVATAR).ToImageSkia();
     94   }
     95 
     96   g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this);
     97 
     98   // Subscribe to authentication error changes so that the avatar button can
     99   // update itself.  Note that guest mode profiles won't have a token service.
    100   SigninErrorController* error =
    101       profiles::GetSigninErrorController(browser_->profile());
    102   if (error) {
    103     error->AddObserver(this);
    104     // This calls UpdateAvatarButtonAndRelayoutParent().
    105     OnErrorChanged();
    106   } else {
    107     UpdateAvatarButtonAndRelayoutParent();
    108   }
    109   SchedulePaint();
    110 }
    111 
    112 NewAvatarButton::~NewAvatarButton() {
    113   g_browser_process->profile_manager()->
    114       GetProfileInfoCache().RemoveObserver(this);
    115   SigninErrorController* error =
    116       profiles::GetSigninErrorController(browser_->profile());
    117   if (error)
    118     error->RemoveObserver(this);
    119 }
    120 
    121 bool NewAvatarButton::OnMousePressed(const ui::MouseEvent& event) {
    122   // Prevent the bubble from being re-shown if it's already showing.
    123   suppress_mouse_released_action_ = ProfileChooserView::IsShowing();
    124   return LabelButton::OnMousePressed(event);
    125 }
    126 
    127 void NewAvatarButton::OnMouseReleased(const ui::MouseEvent& event) {
    128   if (suppress_mouse_released_action_)
    129     suppress_mouse_released_action_ = false;
    130   else
    131     LabelButton::OnMouseReleased(event);
    132 }
    133 
    134 void NewAvatarButton::OnProfileAdded(const base::FilePath& profile_path) {
    135   UpdateAvatarButtonAndRelayoutParent();
    136 }
    137 
    138 void NewAvatarButton::OnProfileWasRemoved(
    139       const base::FilePath& profile_path,
    140       const base::string16& profile_name) {
    141   UpdateAvatarButtonAndRelayoutParent();
    142 }
    143 
    144 void NewAvatarButton::OnProfileNameChanged(
    145       const base::FilePath& profile_path,
    146       const base::string16& old_profile_name) {
    147   UpdateAvatarButtonAndRelayoutParent();
    148 }
    149 
    150 void NewAvatarButton::OnProfileAvatarChanged(
    151       const base::FilePath& profile_path) {
    152   UpdateAvatarButtonAndRelayoutParent();
    153 }
    154 
    155 void NewAvatarButton::OnProfileSupervisedUserIdChanged(
    156       const base::FilePath& profile_path) {
    157   UpdateAvatarButtonAndRelayoutParent();
    158 }
    159 
    160 void NewAvatarButton::OnErrorChanged() {
    161   // If there is an error, show an warning icon.
    162   const SigninErrorController* error =
    163       profiles::GetSigninErrorController(browser_->profile());
    164   has_auth_error_ = error && error->HasError();
    165 
    166   UpdateAvatarButtonAndRelayoutParent();
    167 }
    168 
    169 void NewAvatarButton::UpdateAvatarButtonAndRelayoutParent() {
    170   const ProfileInfoCache& cache =
    171       g_browser_process->profile_manager()->GetProfileInfoCache();
    172 
    173   // If we have a single local profile, then use the generic avatar
    174   // button instead of the profile name. Never use the generic button if
    175   // the active profile is Guest.
    176   bool use_generic_button = (!browser_->profile()->IsGuestSession() &&
    177                              cache.GetNumberOfProfiles() == 1 &&
    178                              cache.GetUserNameOfProfileAtIndex(0).empty());
    179 
    180   SetText(use_generic_button ? base::string16() :
    181       profiles::GetAvatarButtonTextForProfile(browser_->profile()));
    182   // We want the button to resize if the new text is shorter.
    183   SetMinSize(gfx::Size());
    184 
    185   if (use_generic_button) {
    186     SetImage(views::Button::STATE_NORMAL, generic_avatar_);
    187   } else if (has_auth_error_) {
    188     SetImage(views::Button::STATE_NORMAL,
    189              *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
    190                   IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR).ToImageSkia());
    191   } else {
    192     SetImage(views::Button::STATE_NORMAL, gfx::ImageSkia());
    193   }
    194 
    195   // If we are not using the generic button, then reset the spacing between
    196   // the text and the possible authentication error icon.
    197   const int kDefaultImageTextSpacing = 5;
    198   SetImageLabelSpacing(use_generic_button ? 0 : kDefaultImageTextSpacing);
    199 
    200   InvalidateLayout();
    201 
    202   // Because the width of the button might have changed, the parent browser
    203   // frame needs to recalculate the button bounds and redraw it.
    204   if (parent())
    205     parent()->Layout();
    206 }
    207