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.
InitMenuButtonProperties(views::MenuButton * menu_button)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
SetMenuButtonFont(views::MenuButton * menu_button,const gfx::Font & font)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:
NetworkControlReportOnActivate(bool browser_mode,gfx::NativeWindow parent_window,NetworkScreenDelegate * delegate)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:
Activate()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:
NotifyingMenuButton(views::ButtonListener * listener,const std::wstring & text,views::ViewMenuDelegate * menu_delegate,bool show_menu_marker,NetworkScreenDelegate * delegate)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:
OnFocus()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
NetworkSelectionView(NetworkScreenDelegate * delegate)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
~NetworkSelectionView()166 NetworkSelectionView::~NetworkSelectionView() {
167 throbber_->Stop();
168 throbber_ = NULL;
169 }
170
AddControlsToLayout(views::GridLayout * contents_layout)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
InitLayout()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
Init()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
UpdateLocalizedStringsAndFonts()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
OnKeyPressed(const views::KeyEvent &)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
OnLocaleChanged()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
GetNativeWindow() const397 gfx::NativeWindow NetworkSelectionView::GetNativeWindow() const {
398 return
399 GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView());
400 }
401
GetNetworkControlView() const402 views::View* NetworkSelectionView::GetNetworkControlView() const {
403 return network_dropdown_;
404 }
405
ShowConnectingStatus(bool connecting,const string16 & network_id)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
IsConnecting() const429 bool NetworkSelectionView::IsConnecting() const {
430 return connecting_network_label_->IsVisible();
431 }
432
EnableContinue(bool enabled)433 void NetworkSelectionView::EnableContinue(bool enabled) {
434 if (continue_button_)
435 continue_button_->SetEnabled(enabled);
436 }
437
IsContinueEnabled() const438 bool NetworkSelectionView::IsContinueEnabled() const {
439 return continue_button_ && continue_button_->IsEnabled();
440 }
441
442 ////////////////////////////////////////////////////////////////////////////////
443 // views::LinkController implementation:
444
LinkActivated(views::Link * source,int)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
RecreateNativeControls()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
UpdateConnectingNetworkLabel()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