普通文本  |  477行  |  17.47 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/network_selection_view.h"

#include <signal.h>
#include <sys/types.h>
#include <string>

#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/keyboard_switch_menu.h"
#include "chrome/browser/chromeos/login/language_switch_menu.h"
#include "chrome/browser/chromeos/login/network_screen_delegate.h"
#include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
#include "chrome/browser/chromeos/login/rounded_rect_painter.h"
#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
#include "chrome/browser/chromeos/status/network_dropdown_button.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/size.h"
#include "views/controls/label.h"
#include "views/controls/throbber.h"
#include "views/layout/fill_layout.h"
#include "views/layout/grid_layout.h"
#include "views/layout/layout_constants.h"
#include "views/widget/widget.h"
#include "views/widget/widget_gtk.h"
#include "views/window/non_client_view.h"
#include "views/window/window.h"
#include "views/window/window_gtk.h"

using views::Background;
using views::GridLayout;
using views::Label;
using views::View;
using views::Widget;
using views::WidgetGtk;

namespace {

enum kLayoutColumnsets {
  STANDARD_ROW,
  THROBBER_ROW,
};

enum kContentsLayoutColumnsets {
  WELCOME_ROW,
  CONTENTS_ROW,
};

// Grid layout constants.
const int kBorderSize = 10;
const int kWelcomeTitlePadding = 10;
const int kPaddingColumnWidth = 55;
const int kMediumPaddingColumnWidth = 20;
const int kControlPaddingRow = 15;

// Fixed size for language/keyboard/network controls height.
const int kSelectionBoxHeight = 29;

// Menu button is drawn using our custom icons in resources. See
// TextButtonBorder::OnPaint() for details. So this offset compensate
// horizontal size, eaten by those icons.
const int kMenuHorizontalOffset = -3;

// Vertical addition to the menu window to make it appear exactly below
// MenuButton.
const int kMenuVerticalOffset = -1;

// Offset that compensates menu width so that it matches
// menu button visual width when being in pushed state.
const int kMenuWidthOffset = 6;

const SkColor kWelcomeColor = 0xFFCDD3D6;

// Initializes menu button default properties.
static void InitMenuButtonProperties(views::MenuButton* menu_button) {
  menu_button->SetFocusable(true);
  menu_button->SetNormalHasBorder(true);
  menu_button->SetEnabledColor(SK_ColorBLACK);
  menu_button->SetHighlightColor(SK_ColorBLACK);
  menu_button->SetHoverColor(SK_ColorBLACK);
  menu_button->set_animate_on_state_change(false);
  // Menu is positioned by bottom right corner of the MenuButton.
  menu_button->set_menu_offset(kMenuHorizontalOffset, kMenuVerticalOffset);
}

static void SetMenuButtonFont(views::MenuButton* menu_button,
                              const gfx::Font& font) {
  menu_button->SetFont(font);
  chromeos::CorrectMenuButtonFontSize(menu_button);
}

}  // namespace

namespace chromeos {

// NetworkDropdownButton with custom Activate() behavior.
class NetworkControlReportOnActivate : public NetworkDropdownButton {
 public:
  NetworkControlReportOnActivate(bool browser_mode,
                                 gfx::NativeWindow parent_window,
                                 NetworkScreenDelegate* delegate)
      : NetworkDropdownButton(browser_mode, parent_window),
        delegate_(delegate) {}

  // Overridden from MenuButton:
  virtual bool Activate() {
    delegate_->ClearErrors();
    return MenuButton::Activate();
  }

 private:
  NetworkScreenDelegate* delegate_;

  DISALLOW_COPY_AND_ASSIGN(NetworkControlReportOnActivate);
};

// MenuButton with custom processing on focus events.
class NotifyingMenuButton : public DropDownButton {
 public:
  NotifyingMenuButton(views::ButtonListener* listener,
                      const std::wstring& text,
                      views::ViewMenuDelegate* menu_delegate,
                      bool show_menu_marker,
                      NetworkScreenDelegate* delegate)
      : DropDownButton(listener, text, menu_delegate, show_menu_marker),
        delegate_(delegate) {}

  // Overridden from View:
  virtual void OnFocus() OVERRIDE {
    delegate_->ClearErrors();
    GetWidget()->NotifyAccessibilityEvent(
        this, ui::AccessibilityTypes::EVENT_FOCUS, true);
  }

 private:
  NetworkScreenDelegate* delegate_;

  DISALLOW_COPY_AND_ASSIGN(NotifyingMenuButton);
};

NetworkSelectionView::NetworkSelectionView(NetworkScreenDelegate* delegate)
    : entire_screen_view_(NULL),
      contents_view_(NULL),
      languages_menubutton_(NULL),
      keyboards_menubutton_(NULL),
      welcome_label_(NULL),
      select_language_label_(NULL),
      select_keyboard_label_(NULL),
      select_network_label_(NULL),
      connecting_network_label_(NULL),
      network_dropdown_(NULL),
      continue_button_(NULL),
      throbber_(CreateDefaultSmoothedThrobber()),
      proxy_settings_link_(NULL),
      show_keyboard_button_(false),
      delegate_(delegate) {
}

NetworkSelectionView::~NetworkSelectionView() {
  throbber_->Stop();
  throbber_ = NULL;
}

void NetworkSelectionView::AddControlsToLayout(
    views::GridLayout* contents_layout) {
  // Padding rows will be resized.
  const int kPadding = 0;
  if (IsConnecting()) {
    contents_layout->AddPaddingRow(1, kPadding);
    contents_layout->StartRow(0, THROBBER_ROW);
    contents_layout->AddView(connecting_network_label_);
    contents_layout->AddView(throbber_);
    contents_layout->AddPaddingRow(1, kPadding);
  } else {
    contents_layout->AddPaddingRow(1, kPadding);
    contents_layout->StartRow(0, STANDARD_ROW);
    contents_layout->AddView(select_language_label_);
    contents_layout->AddView(languages_menubutton_, 1, 1,
                    GridLayout::FILL, GridLayout::FILL,
                    languages_menubutton_->GetPreferredSize().width(),
                    kSelectionBoxHeight);
    if (show_keyboard_button_) {
      contents_layout->AddPaddingRow(0, kControlPaddingRow);
      contents_layout->StartRow(0, STANDARD_ROW);
      contents_layout->AddView(select_keyboard_label_);
      contents_layout->AddView(
          keyboards_menubutton_, 1, 1,
          GridLayout::FILL, GridLayout::FILL,
          keyboards_menubutton_->GetPreferredSize().width(),
          kSelectionBoxHeight);
    }
    contents_layout->AddPaddingRow(0, kControlPaddingRow);
    contents_layout->StartRow(0, STANDARD_ROW);
    contents_layout->AddView(select_network_label_);
    contents_layout->AddView(network_dropdown_, 1, 1,
                    GridLayout::FILL, GridLayout::FILL,
                    network_dropdown_->GetPreferredSize().width(),
                    kSelectionBoxHeight);
    contents_layout->AddPaddingRow(0, kControlPaddingRow);
    contents_layout->StartRow(0, STANDARD_ROW);
    contents_layout->SkipColumns(1);
    contents_layout->AddView(proxy_settings_link_, 1, 1,
                    GridLayout::LEADING, GridLayout::CENTER);
    contents_layout->AddPaddingRow(0, kControlPaddingRow);
    contents_layout->StartRow(0, STANDARD_ROW);
    contents_layout->SkipColumns(1);
    contents_layout->AddView(continue_button_, 1, 1,
                    GridLayout::LEADING, GridLayout::CENTER);
    contents_layout->AddPaddingRow(1, kPadding);
  }
}

void NetworkSelectionView::InitLayout() {
  gfx::Size screen_size = delegate_->size();
  const int widest_label = std::max(
      std::max(
          select_language_label_->GetPreferredSize().width(),
          select_keyboard_label_->GetPreferredSize().width()),
      select_network_label_->GetPreferredSize().width());
  const int dropdown_width = screen_size.width() - 2 * kBorderSize -
      2 * kPaddingColumnWidth - kMediumPaddingColumnWidth - widest_label;
  delegate_->language_switch_menu()->SetFirstLevelMenuWidth(
      dropdown_width - kMenuWidthOffset);
  delegate_->keyboard_switch_menu()->SetMinimumWidth(
      dropdown_width - kMenuWidthOffset);
  network_dropdown_->SetFirstLevelMenuWidth(dropdown_width - kMenuWidthOffset);

  // Define layout and column set for entire screen (title + screen).
  SetLayoutManager(new views::FillLayout);
  views::GridLayout* screen_layout = new views::GridLayout(entire_screen_view_);
  entire_screen_view_->SetLayoutManager(screen_layout);

  views::ColumnSet* column_set = screen_layout->AddColumnSet(WELCOME_ROW);
  const int welcome_width = screen_size.width() - 2 * kWelcomeTitlePadding -
      2 * kBorderSize;
  column_set->AddPaddingColumn(0, kWelcomeTitlePadding + kBorderSize);
  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
                        GridLayout::FIXED, welcome_width, welcome_width);
  column_set->AddPaddingColumn(0, kWelcomeTitlePadding + kBorderSize);
  column_set = screen_layout->AddColumnSet(CONTENTS_ROW);
  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
      GridLayout::FIXED, screen_size.width(), screen_size.width());
  screen_layout->StartRow(0, WELCOME_ROW);
  screen_layout->AddView(welcome_label_);
  screen_layout->StartRow(1, CONTENTS_ROW);
  screen_layout->AddView(contents_view_);

  // Define layout and column set for screen contents.
  views::GridLayout* contents_layout = new views::GridLayout(contents_view_);
  contents_view_->SetLayoutManager(contents_layout);

  column_set = contents_layout->AddColumnSet(STANDARD_ROW);
  column_set->AddPaddingColumn(1, kPaddingColumnWidth);
  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
                        GridLayout::FIXED, widest_label, widest_label);
  column_set->AddPaddingColumn(0, kMediumPaddingColumnWidth);
  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
                        GridLayout::FIXED, dropdown_width, dropdown_width);
  column_set->AddPaddingColumn(1, kPaddingColumnWidth);

  const int h_padding = 30;
  column_set = contents_layout->AddColumnSet(THROBBER_ROW);
  column_set->AddPaddingColumn(1, h_padding);
  column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
                        GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
  column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
                        GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(1, h_padding);

  AddControlsToLayout(contents_layout);
}

void NetworkSelectionView::Init() {
  contents_view_ = new views::View();

  entire_screen_view_ = new views::View();
  AddChildView(entire_screen_view_);

  // Use rounded rect background.
  views::Painter* painter = CreateWizardPainter(
      &BorderDefinition::kScreenBorder);
  contents_view_->set_background(
      views::Background::CreateBackgroundPainter(true, painter));

  welcome_label_ = new views::Label();
  welcome_label_->SetColor(kWelcomeColor);
  welcome_label_->SetMultiLine(true);

  select_language_label_ = new views::Label();

  languages_menubutton_ = new NotifyingMenuButton(
      NULL, std::wstring(), delegate_->language_switch_menu(), true, delegate_);
  InitMenuButtonProperties(languages_menubutton_);

  select_keyboard_label_ = new views::Label();

  keyboards_menubutton_ = new DropDownButton(
      NULL /* listener */, L"", delegate_->keyboard_switch_menu(),
      true /* show_menu_marker */);
  InitMenuButtonProperties(keyboards_menubutton_);

  select_network_label_ = new views::Label();

  network_dropdown_ = new NetworkControlReportOnActivate(false,
                                                         GetNativeWindow(),
                                                         delegate_);
  InitMenuButtonProperties(network_dropdown_);

  connecting_network_label_ = new views::Label();
  connecting_network_label_->SetVisible(false);

  proxy_settings_link_ = new views::Link();
  proxy_settings_link_->SetController(this);
  proxy_settings_link_->SetVisible(true);
  proxy_settings_link_->SetFocusable(true);
  proxy_settings_link_->SetNormalColor(login::kLinkColor);
  proxy_settings_link_->SetHighlightedColor(login::kLinkColor);

  UpdateLocalizedStringsAndFonts();
}

void NetworkSelectionView::UpdateLocalizedStringsAndFonts() {
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  gfx::Font welcome_label_font = rb.GetFont(ResourceBundle::LargeFont).
      DeriveFont(kWelcomeTitleFontDelta, gfx::Font::BOLD);
  gfx::Font select_label_font = rb.GetFont(ResourceBundle::MediumFont).
      DeriveFont(kNetworkSelectionLabelFontDelta);
  const gfx::Font& base_font = rb.GetFont(ResourceBundle::BaseFont);

  SetMenuButtonFont(languages_menubutton_, base_font);
  languages_menubutton_->SetText(
      UTF16ToWide(delegate_->language_switch_menu()->GetCurrentLocaleName()));
  SetMenuButtonFont(keyboards_menubutton_, base_font);
  keyboards_menubutton_->SetText(
      UTF16ToWide(delegate_->keyboard_switch_menu()->GetCurrentKeyboardName()));
  welcome_label_->SetFont(welcome_label_font);
  welcome_label_->SetText(
      UTF16ToWide(l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_TITLE)));
  select_language_label_->SetFont(select_label_font);
  select_language_label_->SetText(
      UTF16ToWide(l10n_util::GetStringUTF16(IDS_LANGUAGE_SELECTION_SELECT)));
  languages_menubutton_->SetAccessibleName(
      l10n_util::GetStringUTF16(IDS_LANGUAGE_SELECTION_SELECT));
  select_keyboard_label_->SetFont(select_label_font);
  select_keyboard_label_->SetText(
      UTF16ToWide(l10n_util::GetStringUTF16(IDS_KEYBOARD_SELECTION_SELECT)));
  keyboards_menubutton_->SetAccessibleName(
      l10n_util::GetStringUTF16(IDS_KEYBOARD_SELECTION_SELECT));
  select_network_label_->SetFont(select_label_font);
  select_network_label_->SetText(
      UTF16ToWide(l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_SELECT)));
  SetMenuButtonFont(network_dropdown_, base_font);
  network_dropdown_->SetAccessibleName(
      l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_SELECT));
  proxy_settings_link_->SetFont(base_font);
  proxy_settings_link_->SetText(UTF16ToWide(
      l10n_util::GetStringUTF16(IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON)));
  connecting_network_label_->SetFont(rb.GetFont(ResourceBundle::MediumFont));
  RecreateNativeControls();
  UpdateConnectingNetworkLabel();
  network_dropdown_->Refresh();
  InitLayout();
}

////////////////////////////////////////////////////////////////////////////////
// views::View: implementation:

bool NetworkSelectionView::OnKeyPressed(const views::KeyEvent&) {
  if (delegate_->is_error_shown()) {
    delegate_->ClearErrors();
    return true;
  }
  return false;
}

void NetworkSelectionView::OnLocaleChanged() {
  show_keyboard_button_ = true;
  UpdateLocalizedStringsAndFonts();
  // Proxy settings dialog contains localized title.  Zap it.
  proxy_settings_dialog_.reset(NULL);

  Layout();
  SchedulePaint();
}

////////////////////////////////////////////////////////////////////////////////
// NetworkSelectionView, public:

gfx::NativeWindow NetworkSelectionView::GetNativeWindow() const {
  return
      GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView());
}

views::View* NetworkSelectionView::GetNetworkControlView() const {
  return network_dropdown_;
}

void NetworkSelectionView::ShowConnectingStatus(bool connecting,
                                                const string16& network_id) {
  network_id_ = network_id;
  UpdateConnectingNetworkLabel();
  select_language_label_->SetVisible(!connecting);
  languages_menubutton_->SetVisible(!connecting);
  select_keyboard_label_->SetVisible(!connecting);
  keyboards_menubutton_->SetVisible(!connecting);
  select_network_label_->SetVisible(!connecting);
  network_dropdown_->SetVisible(!connecting);
  continue_button_->SetVisible(!connecting);
  proxy_settings_link_->SetVisible(!connecting);
  connecting_network_label_->SetVisible(connecting);
  InitLayout();
  Layout();
  if (connecting) {
    throbber_->Start();
    network_dropdown_->CancelMenu();
  } else {
    throbber_->Stop();
  }
}

bool NetworkSelectionView::IsConnecting() const {
  return connecting_network_label_->IsVisible();
}

void NetworkSelectionView::EnableContinue(bool enabled) {
  if (continue_button_)
    continue_button_->SetEnabled(enabled);
}

bool NetworkSelectionView::IsContinueEnabled() const {
  return continue_button_ && continue_button_->IsEnabled();
}

////////////////////////////////////////////////////////////////////////////////
// views::LinkController implementation:

void NetworkSelectionView::LinkActivated(views::Link* source, int) {
  delegate_->ClearErrors();
  if (source == proxy_settings_link_) {
    if (!proxy_settings_dialog_.get()) {
      proxy_settings_dialog_.reset(
          new ProxySettingsDialog(this, GetNativeWindow()));
    }
    proxy_settings_dialog_->Show();
  }
}

////////////////////////////////////////////////////////////////////////////////
// NetworkSelectionView, private:

void NetworkSelectionView::RecreateNativeControls() {
  // There is no way to get native button preferred size after the button was
  // sized so delete and recreate the button on text update.
  bool is_continue_enabled = IsContinueEnabled();
  delete continue_button_;
  continue_button_ = new login::WideButton(
      delegate_,
      UTF16ToWide(
          l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_CONTINUE_BUTTON)));
  continue_button_->SetEnabled(is_continue_enabled);
}

void NetworkSelectionView::UpdateConnectingNetworkLabel() {
  connecting_network_label_->SetText(UTF16ToWide(l10n_util::GetStringFUTF16(
      IDS_NETWORK_SELECTION_CONNECTING, network_id_)));
}

}  // namespace chromeos