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 37using views::Background; 38using views::GridLayout; 39using views::Label; 40using views::View; 41using views::Widget; 42using views::WidgetGtk; 43 44namespace { 45 46enum kLayoutColumnsets { 47 STANDARD_ROW, 48 THROBBER_ROW, 49}; 50 51enum kContentsLayoutColumnsets { 52 WELCOME_ROW, 53 CONTENTS_ROW, 54}; 55 56// Grid layout constants. 57const int kBorderSize = 10; 58const int kWelcomeTitlePadding = 10; 59const int kPaddingColumnWidth = 55; 60const int kMediumPaddingColumnWidth = 20; 61const int kControlPaddingRow = 15; 62 63// Fixed size for language/keyboard/network controls height. 64const 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. 69const int kMenuHorizontalOffset = -3; 70 71// Vertical addition to the menu window to make it appear exactly below 72// MenuButton. 73const int kMenuVerticalOffset = -1; 74 75// Offset that compensates menu width so that it matches 76// menu button visual width when being in pushed state. 77const int kMenuWidthOffset = 6; 78 79const SkColor kWelcomeColor = 0xFFCDD3D6; 80 81// Initializes menu button default properties. 82static 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 93static 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 101namespace chromeos { 102 103// NetworkDropdownButton with custom Activate() behavior. 104class 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. 125class 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 148NetworkSelectionView::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 166NetworkSelectionView::~NetworkSelectionView() { 167 throbber_->Stop(); 168 throbber_ = NULL; 169} 170 171void 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 220void 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 281void 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 330void 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 376bool NetworkSelectionView::OnKeyPressed(const views::KeyEvent&) { 377 if (delegate_->is_error_shown()) { 378 delegate_->ClearErrors(); 379 return true; 380 } 381 return false; 382} 383 384void 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 397gfx::NativeWindow NetworkSelectionView::GetNativeWindow() const { 398 return 399 GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView()); 400} 401 402views::View* NetworkSelectionView::GetNetworkControlView() const { 403 return network_dropdown_; 404} 405 406void 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 429bool NetworkSelectionView::IsConnecting() const { 430 return connecting_network_label_->IsVisible(); 431} 432 433void NetworkSelectionView::EnableContinue(bool enabled) { 434 if (continue_button_) 435 continue_button_->SetEnabled(enabled); 436} 437 438bool NetworkSelectionView::IsContinueEnabled() const { 439 return continue_button_ && continue_button_->IsEnabled(); 440} 441 442//////////////////////////////////////////////////////////////////////////////// 443// views::LinkController implementation: 444 445void 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 459void 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 471void NetworkSelectionView::UpdateConnectingNetworkLabel() { 472 connecting_network_label_->SetText(UTF16ToWide(l10n_util::GetStringFUTF16( 473 IDS_NETWORK_SELECTION_CONNECTING, network_id_))); 474} 475 476} // namespace chromeos 477