1// Copyright (c) 2012 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/options/network_config_view.h"
6
7#include <algorithm>
8
9#include "ash/shell.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
13#include "chrome/browser/chromeos/options/network_property_ui_data.h"
14#include "chrome/browser/chromeos/options/vpn_config_view.h"
15#include "chrome/browser/chromeos/options/wifi_config_view.h"
16#include "chrome/browser/chromeos/options/wimax_config_view.h"
17#include "chrome/browser/profiles/profile_manager.h"
18#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/browser_finder.h"
20#include "chrome/browser/ui/browser_window.h"
21#include "chrome/browser/ui/host_desktop.h"
22#include "chrome/grit/generated_resources.h"
23#include "chrome/grit/locale_settings.h"
24#include "chrome/grit/theme_resources.h"
25#include "chromeos/login/login_state.h"
26#include "chromeos/network/network_state.h"
27#include "chromeos/network/network_state_handler.h"
28#include "components/user_manager/user.h"
29#include "ui/accessibility/ax_view_state.h"
30#include "ui/aura/window_event_dispatcher.h"
31#include "ui/base/l10n/l10n_util.h"
32#include "ui/base/resource/resource_bundle.h"
33#include "ui/gfx/image/image.h"
34#include "ui/gfx/rect.h"
35#include "ui/views/controls/button/label_button.h"
36#include "ui/views/controls/image_view.h"
37#include "ui/views/layout/layout_constants.h"
38#include "ui/views/widget/widget.h"
39
40using views::Widget;
41
42namespace {
43
44gfx::NativeWindow GetParentForUnhostedDialog() {
45  if (chromeos::LoginDisplayHostImpl::default_host()) {
46    return chromeos::LoginDisplayHostImpl::default_host()->GetNativeWindow();
47  } else {
48    Browser* browser = chrome::FindTabbedBrowser(
49        ProfileManager::GetPrimaryUserProfile(),
50        true,
51        chrome::HOST_DESKTOP_TYPE_ASH);
52    if (browser)
53      return browser->window()->GetNativeWindow();
54  }
55  return NULL;
56}
57
58// Avoid global static initializer.
59chromeos::NetworkConfigView** GetActiveDialogPointer() {
60  static chromeos::NetworkConfigView* active_dialog = NULL;
61  return &active_dialog;
62}
63
64chromeos::NetworkConfigView* GetActiveDialog() {
65  return *(GetActiveDialogPointer());
66}
67
68void SetActiveDialog(chromeos::NetworkConfigView* dialog) {
69  *(GetActiveDialogPointer()) = dialog;
70}
71
72}  // namespace
73
74namespace chromeos {
75
76// static
77const int ChildNetworkConfigView::kInputFieldMinWidth = 270;
78
79NetworkConfigView::NetworkConfigView()
80    : child_config_view_(NULL),
81      delegate_(NULL),
82      advanced_button_(NULL) {
83  DCHECK(GetActiveDialog() == NULL);
84  SetActiveDialog(this);
85}
86
87bool NetworkConfigView::InitWithNetworkState(const NetworkState* network) {
88  DCHECK(network);
89  std::string service_path = network->path();
90  if (network->type() == shill::kTypeWifi ||
91      network->type() == shill::kTypeEthernet) {
92    child_config_view_ = new WifiConfigView(this, service_path, false);
93  } else if (network->type() == shill::kTypeWimax) {
94    child_config_view_ = new WimaxConfigView(this, service_path);
95  } else if (network->type() == shill::kTypeVPN) {
96    child_config_view_ = new VPNConfigView(this, service_path);
97  }
98  return child_config_view_ != NULL;
99}
100
101bool NetworkConfigView::InitWithType(const std::string& type) {
102  if (type == shill::kTypeWifi) {
103    child_config_view_ = new WifiConfigView(this,
104                                            "" /* service_path */,
105                                            false /* show_8021x */);
106    advanced_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(
107        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_ADVANCED_BUTTON));
108    advanced_button_->SetStyle(views::Button::STYLE_BUTTON);
109  } else if (type == shill::kTypeVPN) {
110    child_config_view_ = new VPNConfigView(this,
111                                           "" /* service_path */);
112  }
113  return child_config_view_ != NULL;
114}
115
116NetworkConfigView::~NetworkConfigView() {
117  DCHECK(GetActiveDialog() == this);
118  SetActiveDialog(NULL);
119}
120
121// static
122void NetworkConfigView::Show(const std::string& service_path,
123                             gfx::NativeWindow parent) {
124  if (GetActiveDialog() != NULL)
125    return;
126  NetworkConfigView* view = new NetworkConfigView();
127  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
128      GetNetworkState(service_path);
129  if (!network) {
130    LOG(ERROR) << "NetworkConfigView::Show called with invalid service_path";
131    return;
132  }
133  if (!view->InitWithNetworkState(network)) {
134    LOG(ERROR) << "NetworkConfigView::Show called with invalid network type: "
135               << network->type();
136    delete view;
137    return;
138  }
139  view->ShowDialog(parent);
140}
141
142// static
143void NetworkConfigView::ShowForType(const std::string& type,
144                                    gfx::NativeWindow parent) {
145  if (GetActiveDialog() != NULL)
146    return;
147  NetworkConfigView* view = new NetworkConfigView();
148  if (!view->InitWithType(type)) {
149    LOG(ERROR) << "NetworkConfigView::ShowForType called with invalid type: "
150               << type;
151    delete view;
152    return;
153  }
154  view->ShowDialog(parent);
155}
156
157gfx::NativeWindow NetworkConfigView::GetNativeWindow() const {
158  return GetWidget()->GetNativeWindow();
159}
160
161base::string16 NetworkConfigView::GetDialogButtonLabel(
162    ui::DialogButton button) const {
163  if (button == ui::DIALOG_BUTTON_OK) {
164    if (child_config_view_->IsConfigureDialog())
165      return l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_CONFIGURE);
166    return l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_CONNECT);
167  }
168  return views::DialogDelegateView::GetDialogButtonLabel(button);
169}
170
171bool NetworkConfigView::IsDialogButtonEnabled(ui::DialogButton button) const {
172  // Disable connect button if cannot login.
173  if (button == ui::DIALOG_BUTTON_OK)
174    return child_config_view_->CanLogin();
175  return true;
176}
177
178bool NetworkConfigView::Cancel() {
179  if (delegate_)
180    delegate_->OnDialogCancelled();
181  child_config_view_->Cancel();
182  return true;
183}
184
185bool NetworkConfigView::Accept() {
186  // Do not attempt login if it is guaranteed to fail, keep the dialog open.
187  if (!child_config_view_->CanLogin())
188    return false;
189  bool result = child_config_view_->Login();
190  if (result && delegate_)
191    delegate_->OnDialogAccepted();
192  return result;
193}
194
195views::View* NetworkConfigView::CreateExtraView() {
196  return advanced_button_;
197}
198
199views::View* NetworkConfigView::GetInitiallyFocusedView() {
200  return child_config_view_->GetInitiallyFocusedView();
201}
202
203base::string16 NetworkConfigView::GetWindowTitle() const {
204  DCHECK(!child_config_view_->GetTitle().empty());
205  return child_config_view_->GetTitle();
206}
207
208ui::ModalType NetworkConfigView::GetModalType() const {
209  return ui::MODAL_TYPE_SYSTEM;
210}
211
212void NetworkConfigView::GetAccessibleState(ui::AXViewState* state) {
213  state->name =
214      l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_OTHER_WIFI_NETWORKS);
215  state->role = ui::AX_ROLE_DIALOG;
216}
217
218void NetworkConfigView::ButtonPressed(views::Button* sender,
219                                      const ui::Event& event) {
220  if (advanced_button_ && sender == advanced_button_) {
221    advanced_button_->SetVisible(false);
222    ShowAdvancedView();
223  }
224}
225
226void NetworkConfigView::ShowAdvancedView() {
227  // Clear out the old widgets and build new ones.
228  RemoveChildView(child_config_view_);
229  delete child_config_view_;
230  // For now, there is only an advanced view for Wi-Fi 802.1X.
231  child_config_view_ = new WifiConfigView(this,
232                                          "" /* service_path */,
233                                          true /* show_8021x */);
234  AddChildView(child_config_view_);
235  // Resize the window to be able to hold the new widgets.
236  gfx::Size size = views::Widget::GetLocalizedContentsSize(
237      IDS_JOIN_WIFI_NETWORK_DIALOG_ADVANCED_WIDTH_CHARS,
238      IDS_JOIN_WIFI_NETWORK_DIALOG_ADVANCED_MINIMUM_HEIGHT_LINES);
239  // Get the new bounds with desired size at the same center point.
240  gfx::Rect bounds = GetWidget()->GetWindowBoundsInScreen();
241  int horiz_padding = bounds.width() - size.width();
242  int vert_padding = bounds.height() - size.height();
243  bounds.Inset(horiz_padding / 2, vert_padding / 2,
244               horiz_padding / 2, vert_padding / 2);
245  GetWidget()->SetBoundsConstrained(bounds);
246  Layout();
247  child_config_view_->InitFocus();
248}
249
250void NetworkConfigView::Layout() {
251  child_config_view_->SetBounds(0, 0, width(), height());
252}
253
254gfx::Size NetworkConfigView::GetPreferredSize() const {
255  gfx::Size result(views::Widget::GetLocalizedContentsSize(
256      IDS_JOIN_WIFI_NETWORK_DIALOG_WIDTH_CHARS,
257      IDS_JOIN_WIFI_NETWORK_DIALOG_MINIMUM_HEIGHT_LINES));
258  gfx::Size size = child_config_view_->GetPreferredSize();
259  result.set_height(size.height());
260  if (size.width() > result.width())
261    result.set_width(size.width());
262  return result;
263}
264
265void NetworkConfigView::ViewHierarchyChanged(
266    const ViewHierarchyChangedDetails& details) {
267  // Can't init before we're inserted into a Container, because we require
268  // a HWND to parent native child controls to.
269  if (details.is_add && details.child == this) {
270    AddChildView(child_config_view_);
271  }
272}
273
274void NetworkConfigView::ShowDialog(gfx::NativeWindow parent) {
275  if (parent == NULL)
276    parent = GetParentForUnhostedDialog();
277  // Failed connections may result in a pop-up with no natural parent window,
278  // so provide a fallback context on the primary display. This is necessary
279  // becase one of parent or context must be non NULL.
280  gfx::NativeWindow context =
281      parent ? NULL : ash::Shell::GetPrimaryRootWindow();
282  Widget* window = DialogDelegate::CreateDialogWidget(this, context, parent);
283  window->SetAlwaysOnTop(true);
284  window->Show();
285}
286
287// ChildNetworkConfigView
288
289ChildNetworkConfigView::ChildNetworkConfigView(
290    NetworkConfigView* parent,
291    const std::string& service_path)
292    : parent_(parent),
293      service_path_(service_path) {
294}
295
296ChildNetworkConfigView::~ChildNetworkConfigView() {
297}
298
299bool ChildNetworkConfigView::IsConfigureDialog() {
300  return false;
301}
302
303// static
304void ChildNetworkConfigView::GetShareStateForLoginState(bool* default_value,
305                                                        bool* modifiable) {
306  *default_value = !LoginState::Get()->UserHasNetworkProfile();
307  // Allow only authenticated user to change the share state.
308  *modifiable = LoginState::Get()->IsUserAuthenticated();
309}
310
311// ControlledSettingIndicatorView
312
313ControlledSettingIndicatorView::ControlledSettingIndicatorView()
314    : managed_(false),
315      image_view_(NULL) {
316  Init();
317}
318
319ControlledSettingIndicatorView::ControlledSettingIndicatorView(
320    const NetworkPropertyUIData& ui_data)
321    : managed_(false),
322      image_view_(NULL) {
323  Init();
324  Update(ui_data);
325}
326
327ControlledSettingIndicatorView::~ControlledSettingIndicatorView() {}
328
329void ControlledSettingIndicatorView::Update(
330    const NetworkPropertyUIData& ui_data) {
331  if (managed_ == ui_data.IsManaged())
332    return;
333
334  managed_ = ui_data.IsManaged();
335  PreferredSizeChanged();
336}
337
338gfx::Size ControlledSettingIndicatorView::GetPreferredSize() const {
339  return (managed_ && visible()) ? image_view_->GetPreferredSize()
340                                 : gfx::Size();
341}
342
343void ControlledSettingIndicatorView::Layout() {
344  image_view_->SetBounds(0, 0, width(), height());
345}
346
347void ControlledSettingIndicatorView::Init() {
348  image_ = ResourceBundle::GetSharedInstance().GetImageNamed(
349      IDR_CONTROLLED_SETTING_MANDATORY).ToImageSkia();
350  image_view_ = new views::ImageView();
351  // Disable |image_view_| so mouse events propagate to the parent.
352  image_view_->SetEnabled(false);
353  image_view_->SetImage(image_);
354  image_view_->SetTooltipText(
355      l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
356  AddChildView(image_view_);
357}
358
359}  // namespace chromeos
360