Home | History | Annotate | Download | only in login
      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/network_selection_view.h"
      6 
      7 #include <signal.h>
      8 #include <sys/types.h>
      9 #include <string>
     10 
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/chromeos/login/helper.h"
     13 #include "chrome/browser/chromeos/login/keyboard_switch_menu.h"
     14 #include "chrome/browser/chromeos/login/language_switch_menu.h"
     15 #include "chrome/browser/chromeos/login/network_screen_delegate.h"
     16 #include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
     17 #include "chrome/browser/chromeos/login/rounded_rect_painter.h"
     18 #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
     19 #include "chrome/browser/chromeos/status/network_dropdown_button.h"
     20 #include "grit/chromium_strings.h"
     21 #include "grit/generated_resources.h"
     22 #include "grit/theme_resources.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "ui/base/resource/resource_bundle.h"
     25 #include "ui/gfx/size.h"
     26 #include "views/controls/label.h"
     27 #include "views/controls/throbber.h"
     28 #include "views/layout/fill_layout.h"
     29 #include "views/layout/grid_layout.h"
     30 #include "views/layout/layout_constants.h"
     31 #include "views/widget/widget.h"
     32 #include "views/widget/widget_gtk.h"
     33 #include "views/window/non_client_view.h"
     34 #include "views/window/window.h"
     35 #include "views/window/window_gtk.h"
     36 
     37 using views::Background;
     38 using views::GridLayout;
     39 using views::Label;
     40 using views::View;
     41 using views::Widget;
     42 using views::WidgetGtk;
     43 
     44 namespace {
     45 
     46 enum kLayoutColumnsets {
     47   STANDARD_ROW,
     48   THROBBER_ROW,
     49 };
     50 
     51 enum kContentsLayoutColumnsets {
     52   WELCOME_ROW,
     53   CONTENTS_ROW,
     54 };
     55 
     56 // Grid layout constants.
     57 const int kBorderSize = 10;
     58 const int kWelcomeTitlePadding = 10;
     59 const int kPaddingColumnWidth = 55;
     60 const int kMediumPaddingColumnWidth = 20;
     61 const int kControlPaddingRow = 15;
     62 
     63 // Fixed size for language/keyboard/network controls height.
     64 const int kSelectionBoxHeight = 29;
     65 
     66 // Menu button is drawn using our custom icons in resources. See
     67 // TextButtonBorder::OnPaint() for details. So this offset compensate
     68 // horizontal size, eaten by those icons.
     69 const int kMenuHorizontalOffset = -3;
     70 
     71 // Vertical addition to the menu window to make it appear exactly below
     72 // MenuButton.
     73 const int kMenuVerticalOffset = -1;
     74 
     75 // Offset that compensates menu width so that it matches
     76 // menu button visual width when being in pushed state.
     77 const int kMenuWidthOffset = 6;
     78 
     79 const SkColor kWelcomeColor = 0xFFCDD3D6;
     80 
     81 // Initializes menu button default properties.
     82 static void InitMenuButtonProperties(views::MenuButton* menu_button) {
     83   menu_button->SetFocusable(true);
     84   menu_button->SetNormalHasBorder(true);
     85   menu_button->SetEnabledColor(SK_ColorBLACK);
     86   menu_button->SetHighlightColor(SK_ColorBLACK);
     87   menu_button->SetHoverColor(SK_ColorBLACK);
     88   menu_button->set_animate_on_state_change(false);
     89   // Menu is positioned by bottom right corner of the MenuButton.
     90   menu_button->set_menu_offset(kMenuHorizontalOffset, kMenuVerticalOffset);
     91 }
     92 
     93 static void SetMenuButtonFont(views::MenuButton* menu_button,
     94                               const gfx::Font& font) {
     95   menu_button->SetFont(font);
     96   chromeos::CorrectMenuButtonFontSize(menu_button);
     97 }
     98 
     99 }  // namespace
    100 
    101 namespace chromeos {
    102 
    103 // NetworkDropdownButton with custom Activate() behavior.
    104 class NetworkControlReportOnActivate : public NetworkDropdownButton {
    105  public:
    106   NetworkControlReportOnActivate(bool browser_mode,
    107                                  gfx::NativeWindow parent_window,
    108                                  NetworkScreenDelegate* delegate)
    109       : NetworkDropdownButton(browser_mode, parent_window),
    110         delegate_(delegate) {}
    111 
    112   // Overridden from MenuButton:
    113   virtual bool Activate() {
    114     delegate_->ClearErrors();
    115     return MenuButton::Activate();
    116   }
    117 
    118  private:
    119   NetworkScreenDelegate* delegate_;
    120 
    121   DISALLOW_COPY_AND_ASSIGN(NetworkControlReportOnActivate);
    122 };
    123 
    124 // MenuButton with custom processing on focus events.
    125 class NotifyingMenuButton : public DropDownButton {
    126  public:
    127   NotifyingMenuButton(views::ButtonListener* listener,
    128                       const std::wstring& text,
    129                       views::ViewMenuDelegate* menu_delegate,
    130                       bool show_menu_marker,
    131                       NetworkScreenDelegate* delegate)
    132       : DropDownButton(listener, text, menu_delegate, show_menu_marker),
    133         delegate_(delegate) {}
    134 
    135   // Overridden from View:
    136   virtual void OnFocus() OVERRIDE {
    137     delegate_->ClearErrors();
    138     GetWidget()->NotifyAccessibilityEvent(
    139         this, ui::AccessibilityTypes::EVENT_FOCUS, true);
    140   }
    141 
    142  private:
    143   NetworkScreenDelegate* delegate_;
    144 
    145   DISALLOW_COPY_AND_ASSIGN(NotifyingMenuButton);
    146 };
    147 
    148 NetworkSelectionView::NetworkSelectionView(NetworkScreenDelegate* delegate)
    149     : entire_screen_view_(NULL),
    150       contents_view_(NULL),
    151       languages_menubutton_(NULL),
    152       keyboards_menubutton_(NULL),
    153       welcome_label_(NULL),
    154       select_language_label_(NULL),
    155       select_keyboard_label_(NULL),
    156       select_network_label_(NULL),
    157       connecting_network_label_(NULL),
    158       network_dropdown_(NULL),
    159       continue_button_(NULL),
    160       throbber_(CreateDefaultSmoothedThrobber()),
    161       proxy_settings_link_(NULL),
    162       show_keyboard_button_(false),
    163       delegate_(delegate) {
    164 }
    165 
    166 NetworkSelectionView::~NetworkSelectionView() {
    167   throbber_->Stop();
    168   throbber_ = NULL;
    169 }
    170 
    171 void NetworkSelectionView::AddControlsToLayout(
    172     views::GridLayout* contents_layout) {
    173   // Padding rows will be resized.
    174   const int kPadding = 0;
    175   if (IsConnecting()) {
    176     contents_layout->AddPaddingRow(1, kPadding);
    177     contents_layout->StartRow(0, THROBBER_ROW);
    178     contents_layout->AddView(connecting_network_label_);
    179     contents_layout->AddView(throbber_);
    180     contents_layout->AddPaddingRow(1, kPadding);
    181   } else {
    182     contents_layout->AddPaddingRow(1, kPadding);
    183     contents_layout->StartRow(0, STANDARD_ROW);
    184     contents_layout->AddView(select_language_label_);
    185     contents_layout->AddView(languages_menubutton_, 1, 1,
    186                     GridLayout::FILL, GridLayout::FILL,
    187                     languages_menubutton_->GetPreferredSize().width(),
    188                     kSelectionBoxHeight);
    189     if (show_keyboard_button_) {
    190       contents_layout->AddPaddingRow(0, kControlPaddingRow);
    191       contents_layout->StartRow(0, STANDARD_ROW);
    192       contents_layout->AddView(select_keyboard_label_);
    193       contents_layout->AddView(
    194           keyboards_menubutton_, 1, 1,
    195           GridLayout::FILL, GridLayout::FILL,
    196           keyboards_menubutton_->GetPreferredSize().width(),
    197           kSelectionBoxHeight);
    198     }
    199     contents_layout->AddPaddingRow(0, kControlPaddingRow);
    200     contents_layout->StartRow(0, STANDARD_ROW);
    201     contents_layout->AddView(select_network_label_);
    202     contents_layout->AddView(network_dropdown_, 1, 1,
    203                     GridLayout::FILL, GridLayout::FILL,
    204                     network_dropdown_->GetPreferredSize().width(),
    205                     kSelectionBoxHeight);
    206     contents_layout->AddPaddingRow(0, kControlPaddingRow);
    207     contents_layout->StartRow(0, STANDARD_ROW);
    208     contents_layout->SkipColumns(1);
    209     contents_layout->AddView(proxy_settings_link_, 1, 1,
    210                     GridLayout::LEADING, GridLayout::CENTER);
    211     contents_layout->AddPaddingRow(0, kControlPaddingRow);
    212     contents_layout->StartRow(0, STANDARD_ROW);
    213     contents_layout->SkipColumns(1);
    214     contents_layout->AddView(continue_button_, 1, 1,
    215                     GridLayout::LEADING, GridLayout::CENTER);
    216     contents_layout->AddPaddingRow(1, kPadding);
    217   }
    218 }
    219 
    220 void NetworkSelectionView::InitLayout() {
    221   gfx::Size screen_size = delegate_->size();
    222   const int widest_label = std::max(
    223       std::max(
    224           select_language_label_->GetPreferredSize().width(),
    225           select_keyboard_label_->GetPreferredSize().width()),
    226       select_network_label_->GetPreferredSize().width());
    227   const int dropdown_width = screen_size.width() - 2 * kBorderSize -
    228       2 * kPaddingColumnWidth - kMediumPaddingColumnWidth - widest_label;
    229   delegate_->language_switch_menu()->SetFirstLevelMenuWidth(
    230       dropdown_width - kMenuWidthOffset);
    231   delegate_->keyboard_switch_menu()->SetMinimumWidth(
    232       dropdown_width - kMenuWidthOffset);
    233   network_dropdown_->SetFirstLevelMenuWidth(dropdown_width - kMenuWidthOffset);
    234 
    235   // Define layout and column set for entire screen (title + screen).
    236   SetLayoutManager(new views::FillLayout);
    237   views::GridLayout* screen_layout = new views::GridLayout(entire_screen_view_);
    238   entire_screen_view_->SetLayoutManager(screen_layout);
    239 
    240   views::ColumnSet* column_set = screen_layout->AddColumnSet(WELCOME_ROW);
    241   const int welcome_width = screen_size.width() - 2 * kWelcomeTitlePadding -
    242       2 * kBorderSize;
    243   column_set->AddPaddingColumn(0, kWelcomeTitlePadding + kBorderSize);
    244   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
    245                         GridLayout::FIXED, welcome_width, welcome_width);
    246   column_set->AddPaddingColumn(0, kWelcomeTitlePadding + kBorderSize);
    247   column_set = screen_layout->AddColumnSet(CONTENTS_ROW);
    248   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
    249       GridLayout::FIXED, screen_size.width(), screen_size.width());
    250   screen_layout->StartRow(0, WELCOME_ROW);
    251   screen_layout->AddView(welcome_label_);
    252   screen_layout->StartRow(1, CONTENTS_ROW);
    253   screen_layout->AddView(contents_view_);
    254 
    255   // Define layout and column set for screen contents.
    256   views::GridLayout* contents_layout = new views::GridLayout(contents_view_);
    257   contents_view_->SetLayoutManager(contents_layout);
    258 
    259   column_set = contents_layout->AddColumnSet(STANDARD_ROW);
    260   column_set->AddPaddingColumn(1, kPaddingColumnWidth);
    261   column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
    262                         GridLayout::FIXED, widest_label, widest_label);
    263   column_set->AddPaddingColumn(0, kMediumPaddingColumnWidth);
    264   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
    265                         GridLayout::FIXED, dropdown_width, dropdown_width);
    266   column_set->AddPaddingColumn(1, kPaddingColumnWidth);
    267 
    268   const int h_padding = 30;
    269   column_set = contents_layout->AddColumnSet(THROBBER_ROW);
    270   column_set->AddPaddingColumn(1, h_padding);
    271   column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
    272                         GridLayout::USE_PREF, 0, 0);
    273   column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
    274   column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
    275                         GridLayout::USE_PREF, 0, 0);
    276   column_set->AddPaddingColumn(1, h_padding);
    277 
    278   AddControlsToLayout(contents_layout);
    279 }
    280 
    281 void NetworkSelectionView::Init() {
    282   contents_view_ = new views::View();
    283 
    284   entire_screen_view_ = new views::View();
    285   AddChildView(entire_screen_view_);
    286 
    287   // Use rounded rect background.
    288   views::Painter* painter = CreateWizardPainter(
    289       &BorderDefinition::kScreenBorder);
    290   contents_view_->set_background(
    291       views::Background::CreateBackgroundPainter(true, painter));
    292 
    293   welcome_label_ = new views::Label();
    294   welcome_label_->SetColor(kWelcomeColor);
    295   welcome_label_->SetMultiLine(true);
    296 
    297   select_language_label_ = new views::Label();
    298 
    299   languages_menubutton_ = new NotifyingMenuButton(
    300       NULL, std::wstring(), delegate_->language_switch_menu(), true, delegate_);
    301   InitMenuButtonProperties(languages_menubutton_);
    302 
    303   select_keyboard_label_ = new views::Label();
    304 
    305   keyboards_menubutton_ = new DropDownButton(
    306       NULL /* listener */, L"", delegate_->keyboard_switch_menu(),
    307       true /* show_menu_marker */);
    308   InitMenuButtonProperties(keyboards_menubutton_);
    309 
    310   select_network_label_ = new views::Label();
    311 
    312   network_dropdown_ = new NetworkControlReportOnActivate(false,
    313                                                          GetNativeWindow(),
    314                                                          delegate_);
    315   InitMenuButtonProperties(network_dropdown_);
    316 
    317   connecting_network_label_ = new views::Label();
    318   connecting_network_label_->SetVisible(false);
    319 
    320   proxy_settings_link_ = new views::Link();
    321   proxy_settings_link_->SetController(this);
    322   proxy_settings_link_->SetVisible(true);
    323   proxy_settings_link_->SetFocusable(true);
    324   proxy_settings_link_->SetNormalColor(login::kLinkColor);
    325   proxy_settings_link_->SetHighlightedColor(login::kLinkColor);
    326 
    327   UpdateLocalizedStringsAndFonts();
    328 }
    329 
    330 void NetworkSelectionView::UpdateLocalizedStringsAndFonts() {
    331   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    332   gfx::Font welcome_label_font = rb.GetFont(ResourceBundle::LargeFont).
    333       DeriveFont(kWelcomeTitleFontDelta, gfx::Font::BOLD);
    334   gfx::Font select_label_font = rb.GetFont(ResourceBundle::MediumFont).
    335       DeriveFont(kNetworkSelectionLabelFontDelta);
    336   const gfx::Font& base_font = rb.GetFont(ResourceBundle::BaseFont);
    337 
    338   SetMenuButtonFont(languages_menubutton_, base_font);
    339   languages_menubutton_->SetText(
    340       UTF16ToWide(delegate_->language_switch_menu()->GetCurrentLocaleName()));
    341   SetMenuButtonFont(keyboards_menubutton_, base_font);
    342   keyboards_menubutton_->SetText(
    343       UTF16ToWide(delegate_->keyboard_switch_menu()->GetCurrentKeyboardName()));
    344   welcome_label_->SetFont(welcome_label_font);
    345   welcome_label_->SetText(
    346       UTF16ToWide(l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_TITLE)));
    347   select_language_label_->SetFont(select_label_font);
    348   select_language_label_->SetText(
    349       UTF16ToWide(l10n_util::GetStringUTF16(IDS_LANGUAGE_SELECTION_SELECT)));
    350   languages_menubutton_->SetAccessibleName(
    351       l10n_util::GetStringUTF16(IDS_LANGUAGE_SELECTION_SELECT));
    352   select_keyboard_label_->SetFont(select_label_font);
    353   select_keyboard_label_->SetText(
    354       UTF16ToWide(l10n_util::GetStringUTF16(IDS_KEYBOARD_SELECTION_SELECT)));
    355   keyboards_menubutton_->SetAccessibleName(
    356       l10n_util::GetStringUTF16(IDS_KEYBOARD_SELECTION_SELECT));
    357   select_network_label_->SetFont(select_label_font);
    358   select_network_label_->SetText(
    359       UTF16ToWide(l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_SELECT)));
    360   SetMenuButtonFont(network_dropdown_, base_font);
    361   network_dropdown_->SetAccessibleName(
    362       l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_SELECT));
    363   proxy_settings_link_->SetFont(base_font);
    364   proxy_settings_link_->SetText(UTF16ToWide(
    365       l10n_util::GetStringUTF16(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON)));
    366   connecting_network_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont));
    367   RecreateNativeControls();
    368   UpdateConnectingNetworkLabel();
    369   network_dropdown_->Refresh();
    370   InitLayout();
    371 }
    372 
    373 ////////////////////////////////////////////////////////////////////////////////
    374 // views::View: implementation:
    375 
    376 bool NetworkSelectionView::OnKeyPressed(const views::KeyEvent&) {
    377   if (delegate_->is_error_shown()) {
    378     delegate_->ClearErrors();
    379     return true;
    380   }
    381   return false;
    382 }
    383 
    384 void NetworkSelectionView::OnLocaleChanged() {
    385   show_keyboard_button_ = true;
    386   UpdateLocalizedStringsAndFonts();
    387   // Proxy settings dialog contains localized title.  Zap it.
    388   proxy_settings_dialog_.reset(NULL);
    389 
    390   Layout();
    391   SchedulePaint();
    392 }
    393 
    394 ////////////////////////////////////////////////////////////////////////////////
    395 // NetworkSelectionView, public:
    396 
    397 gfx::NativeWindow NetworkSelectionView::GetNativeWindow() const {
    398   return
    399       GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView());
    400 }
    401 
    402 views::View* NetworkSelectionView::GetNetworkControlView() const {
    403   return network_dropdown_;
    404 }
    405 
    406 void NetworkSelectionView::ShowConnectingStatus(bool connecting,
    407                                                 const string16& network_id) {
    408   network_id_ = network_id;
    409   UpdateConnectingNetworkLabel();
    410   select_language_label_->SetVisible(!connecting);
    411   languages_menubutton_->SetVisible(!connecting);
    412   select_keyboard_label_->SetVisible(!connecting);
    413   keyboards_menubutton_->SetVisible(!connecting);
    414   select_network_label_->SetVisible(!connecting);
    415   network_dropdown_->SetVisible(!connecting);
    416   continue_button_->SetVisible(!connecting);
    417   proxy_settings_link_->SetVisible(!connecting);
    418   connecting_network_label_->SetVisible(connecting);
    419   InitLayout();
    420   Layout();
    421   if (connecting) {
    422     throbber_->Start();
    423     network_dropdown_->CancelMenu();
    424   } else {
    425     throbber_->Stop();
    426   }
    427 }
    428 
    429 bool NetworkSelectionView::IsConnecting() const {
    430   return connecting_network_label_->IsVisible();
    431 }
    432 
    433 void NetworkSelectionView::EnableContinue(bool enabled) {
    434   if (continue_button_)
    435     continue_button_->SetEnabled(enabled);
    436 }
    437 
    438 bool NetworkSelectionView::IsContinueEnabled() const {
    439   return continue_button_ && continue_button_->IsEnabled();
    440 }
    441 
    442 ////////////////////////////////////////////////////////////////////////////////
    443 // views::LinkController implementation:
    444 
    445 void NetworkSelectionView::LinkActivated(views::Link* source, int) {
    446   delegate_->ClearErrors();
    447   if (source == proxy_settings_link_) {
    448     if (!proxy_settings_dialog_.get()) {
    449       proxy_settings_dialog_.reset(
    450           new ProxySettingsDialog(this, GetNativeWindow()));
    451     }
    452     proxy_settings_dialog_->Show();
    453   }
    454 }
    455 
    456 ////////////////////////////////////////////////////////////////////////////////
    457 // NetworkSelectionView, private:
    458 
    459 void NetworkSelectionView::RecreateNativeControls() {
    460   // There is no way to get native button preferred size after the button was
    461   // sized so delete and recreate the button on text update.
    462   bool is_continue_enabled = IsContinueEnabled();
    463   delete continue_button_;
    464   continue_button_ = new login::WideButton(
    465       delegate_,
    466       UTF16ToWide(
    467           l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_CONTINUE_BUTTON)));
    468   continue_button_->SetEnabled(is_continue_enabled);
    469 }
    470 
    471 void NetworkSelectionView::UpdateConnectingNetworkLabel() {
    472   connecting_network_label_->SetText(UTF16ToWide(l10n_util::GetStringFUTF16(
    473       IDS_NETWORK_SELECTION_CONNECTING, network_id_)));
    474 }
    475 
    476 }  // namespace chromeos
    477