background_view.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/background_view.h"
6
7#include <string>
8#include <vector>
9
10#include "base/string16.h"
11#include "base/string_util.h"
12#include "base/stringprintf.h"
13#include "base/utf_string_conversions.h"
14#include "chrome/browser/chromeos/login/helper.h"
15#include "chrome/browser/chromeos/login/login_utils.h"
16#include "chrome/browser/chromeos/login/oobe_progress_bar.h"
17#include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
18#include "chrome/browser/chromeos/login/rounded_rect_painter.h"
19#include "chrome/browser/chromeos/login/shutdown_button.h"
20#include "chrome/browser/chromeos/login/wizard_controller.h"
21#include "chrome/browser/chromeos/status/clock_menu_button.h"
22#include "chrome/browser/chromeos/status/input_method_menu_button.h"
23#include "chrome/browser/chromeos/status/network_menu_button.h"
24#include "chrome/browser/chromeos/status/status_area_view.h"
25#include "chrome/browser/chromeos/wm_ipc.h"
26#include "chrome/browser/profiles/profile_manager.h"
27#include "chrome/browser/ui/views/dom_view.h"
28#include "chrome/browser/ui/views/window.h"
29#include "googleurl/src/gurl.h"
30#include "grit/chromium_strings.h"
31#include "grit/generated_resources.h"
32#include "grit/theme_resources.h"
33#include "third_party/cros/chromeos_wm_ipc_enums.h"
34#include "ui/base/l10n/l10n_util.h"
35#include "ui/base/resource/resource_bundle.h"
36#include "ui/base/x/x11_util.h"
37#include "ui/gfx/gtk_util.h"
38#include "views/controls/button/text_button.h"
39#include "views/controls/label.h"
40#include "views/screen.h"
41#include "views/widget/widget_gtk.h"
42#include "views/window/window.h"
43
44// X Windows headers have "#define Status int". That interferes with
45// NetworkLibrary header which defines enum "Status".
46#include <X11/cursorfont.h>  // NOLINT
47#include <X11/Xcursor/Xcursor.h>  // NOLINT
48
49using views::WidgetGtk;
50
51namespace {
52
53const SkColor kVersionColor = 0xff5c739f;
54
55// Returns the corresponding step id for step constant.
56int GetStepId(size_t step) {
57  switch (step) {
58    case chromeos::BackgroundView::SELECT_NETWORK:
59      return IDS_OOBE_SELECT_NETWORK;
60    case chromeos::BackgroundView::EULA:
61      return IDS_OOBE_EULA;
62    case chromeos::BackgroundView::SIGNIN:
63      return IDS_OOBE_SIGNIN;
64    case chromeos::BackgroundView::REGISTRATION:
65      return IDS_OOBE_REGISTRATION;
66    case chromeos::BackgroundView::PICTURE:
67      return IDS_OOBE_PICTURE;
68    default:
69      NOTREACHED();
70      return 0;
71  }
72}
73
74// The same as TextButton but switches cursor to hand cursor when mouse
75// is over the button.
76class TextButtonWithHandCursorOver : public views::TextButton {
77 public:
78  TextButtonWithHandCursorOver(views::ButtonListener* listener,
79                               const std::wstring& text)
80      : views::TextButton(listener, text) {
81  }
82
83  virtual ~TextButtonWithHandCursorOver() {}
84
85  virtual gfx::NativeCursor GetCursorForPoint(
86      ui::EventType event_type,
87      const gfx::Point& p) {
88    if (!IsEnabled()) {
89      return NULL;
90    }
91    return gfx::GetCursor(GDK_HAND2);
92  }
93
94 private:
95  DISALLOW_COPY_AND_ASSIGN(TextButtonWithHandCursorOver);
96};
97
98// This gets rid of the ugly X default cursor.
99static void ResetXCursor() {
100  // TODO(sky): nuke this once new window manager is in place.
101  Display* display = ui::GetXDisplay();
102  Cursor cursor = XCreateFontCursor(display, XC_left_ptr);
103  XID root_window = ui::GetX11RootWindow();
104  XSetWindowAttributes attr;
105  attr.cursor = cursor;
106  XChangeWindowAttributes(display, root_window, CWCursor, &attr);
107}
108
109}  // namespace
110
111namespace chromeos {
112
113///////////////////////////////////////////////////////////////////////////////
114// BackgroundView public:
115
116BackgroundView::BackgroundView()
117    : status_area_(NULL),
118      os_version_label_(NULL),
119      boot_times_label_(NULL),
120      progress_bar_(NULL),
121      shutdown_button_(NULL),
122      did_paint_(false),
123#if defined(OFFICIAL_BUILD)
124      is_official_build_(true),
125#else
126      is_official_build_(false),
127#endif
128      background_area_(NULL) {
129}
130
131void BackgroundView::Init(const GURL& background_url) {
132  views::Painter* painter = CreateBackgroundPainter();
133  set_background(views::Background::CreateBackgroundPainter(true, painter));
134  InitStatusArea();
135  InitInfoLabels();
136  if (!background_url.is_empty()) {
137    Profile* profile = ProfileManager::GetDefaultProfile();
138    background_area_ = new DOMView();
139    AddChildView(background_area_);
140    background_area_->Init(profile, NULL);
141    background_area_->SetVisible(false);
142    background_area_->LoadURL(background_url);
143  }
144}
145
146void BackgroundView::EnableShutdownButton(bool enable) {
147  if (enable) {
148    if (shutdown_button_)
149      return;
150    shutdown_button_ = new ShutdownButton();
151    shutdown_button_->Init();
152    AddChildView(shutdown_button_);
153  } else {
154    if (!shutdown_button_)
155      return;
156    delete shutdown_button_;
157    shutdown_button_ = NULL;
158    SchedulePaint();
159  }
160}
161
162// static
163views::Widget* BackgroundView::CreateWindowContainingView(
164    const gfx::Rect& bounds,
165    const GURL& background_url,
166    BackgroundView** view) {
167  ResetXCursor();
168
169  WidgetGtk* window = new WidgetGtk(WidgetGtk::TYPE_WINDOW);
170  window->Init(NULL, bounds);
171  *view = new BackgroundView();
172  (*view)->Init(background_url);
173
174  if ((*view)->ScreenSaverEnabled())
175    (*view)->ShowScreenSaver();
176
177  window->SetContentsView(*view);
178
179  (*view)->UpdateWindowType();
180
181  // This keeps the window from flashing at startup.
182  GdkWindow* gdk_window = window->GetNativeView()->window;
183  gdk_window_set_back_pixmap(gdk_window, NULL, false);
184
185  LoginUtils::Get()->SetBackgroundView(*view);
186
187  return window;
188}
189
190void BackgroundView::CreateModalPopup(views::WindowDelegate* view) {
191  views::Window* window = browser::CreateViewsWindow(
192      GetNativeWindow(), gfx::Rect(), view);
193  window->SetIsAlwaysOnTop(true);
194  window->Show();
195}
196
197gfx::NativeWindow BackgroundView::GetNativeWindow() const {
198  return
199      GTK_WINDOW(static_cast<const WidgetGtk*>(GetWidget())->GetNativeView());
200}
201
202void BackgroundView::SetStatusAreaVisible(bool visible) {
203  status_area_->SetVisible(visible);
204}
205
206void BackgroundView::SetStatusAreaEnabled(bool enable) {
207  status_area_->MakeButtonsActive(enable);
208}
209
210void BackgroundView::SetOobeProgressBarVisible(bool visible) {
211  if (!progress_bar_ && visible)
212    InitProgressBar();
213
214  if (progress_bar_)
215    progress_bar_->SetVisible(visible);
216}
217
218bool BackgroundView::IsOobeProgressBarVisible() {
219  return progress_bar_ && progress_bar_->IsVisible();
220}
221
222void BackgroundView::SetOobeProgress(LoginStep step) {
223  DCHECK(step < STEPS_COUNT);
224  if (progress_bar_)
225    progress_bar_->SetStep(GetStepId(step));
226}
227
228void BackgroundView::ShowScreenSaver() {
229  SetStatusAreaVisible(false);
230  background_area_->SetVisible(true);
231}
232
233void BackgroundView::HideScreenSaver() {
234  SetStatusAreaVisible(true);
235  // TODO(oshima): we need a way to suspend screen saver
236  // to save power when it's not visible.
237  background_area_->SetVisible(false);
238}
239
240bool BackgroundView::IsScreenSaverVisible() {
241  return ScreenSaverEnabled() && background_area_->IsVisible();
242}
243
244bool BackgroundView::ScreenSaverEnabled() {
245  return background_area_ != NULL;
246}
247
248///////////////////////////////////////////////////////////////////////////////
249// BackgroundView protected:
250
251void BackgroundView::Paint(gfx::Canvas* canvas) {
252  views::View::Paint(canvas);
253  if (!did_paint_) {
254    did_paint_ = true;
255    UpdateWindowType();
256  }
257}
258
259void BackgroundView::Layout() {
260  const int kCornerPadding = 5;
261  const int kInfoLeftPadding = 10;
262  const int kInfoBottomPadding = 10;
263  const int kInfoBetweenLinesPadding = 1;
264  const int kProgressBarBottomPadding = 20;
265  const int kProgressBarWidth = 750;
266  const int kProgressBarHeight = 70;
267  gfx::Size status_area_size = status_area_->GetPreferredSize();
268  status_area_->SetBounds(
269      width() - status_area_size.width() - kCornerPadding,
270      kCornerPadding,
271      status_area_size.width(),
272      status_area_size.height());
273  gfx::Size version_size = os_version_label_->GetPreferredSize();
274  int os_version_y = height() - version_size.height() - kInfoBottomPadding;
275  if (!is_official_build_)
276    os_version_y -= (version_size.height() + kInfoBetweenLinesPadding);
277  os_version_label_->SetBounds(
278      kInfoLeftPadding,
279      os_version_y,
280      width() - 2 * kInfoLeftPadding,
281      version_size.height());
282  if (!is_official_build_) {
283    boot_times_label_->SetBounds(
284        kInfoLeftPadding,
285        height() - (version_size.height() + kInfoBottomPadding),
286        width() - 2 * kInfoLeftPadding,
287        version_size.height());
288  }
289  if (progress_bar_) {
290    progress_bar_->SetBounds(
291        (width() - kProgressBarWidth) / 2,
292        (height() - kProgressBarBottomPadding - kProgressBarHeight),
293        kProgressBarWidth,
294        kProgressBarHeight);
295  }
296  if (shutdown_button_) {
297    shutdown_button_->LayoutIn(this);
298  }
299  if (background_area_)
300    background_area_->SetBoundsRect(this->bounds());
301}
302
303void BackgroundView::ChildPreferredSizeChanged(View* child) {
304  Layout();
305  SchedulePaint();
306}
307
308bool BackgroundView::ShouldOpenButtonOptions(
309    const views::View* button_view) const {
310  if (button_view == status_area_->network_view()) {
311    return true;
312  }
313  if (button_view == status_area_->clock_view() ||
314      button_view == status_area_->input_method_view()) {
315    return false;
316  }
317  return true;
318}
319
320void BackgroundView::OpenButtonOptions(const views::View* button_view) {
321  if (button_view == status_area_->network_view()) {
322    if (proxy_settings_dialog_.get() == NULL) {
323      proxy_settings_dialog_.reset(new ProxySettingsDialog(
324          this, GetNativeWindow()));
325    }
326    proxy_settings_dialog_->Show();
327  }
328}
329
330StatusAreaHost::ScreenMode BackgroundView::GetScreenMode() const {
331  return kLoginMode;
332}
333
334// Overridden from LoginHtmlDialog::Delegate:
335void BackgroundView::OnLocaleChanged() {
336  // Proxy settings dialog contains localized strings.
337  proxy_settings_dialog_.reset();
338}
339
340///////////////////////////////////////////////////////////////////////////////
341// BackgroundView private:
342
343void BackgroundView::InitStatusArea() {
344  DCHECK(status_area_ == NULL);
345  status_area_ = new StatusAreaView(this);
346  status_area_->Init();
347  AddChildView(status_area_);
348}
349
350void BackgroundView::InitInfoLabels() {
351  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
352
353  os_version_label_ = new views::Label();
354  os_version_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
355  os_version_label_->SetColor(kVersionColor);
356  os_version_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
357  AddChildView(os_version_label_);
358  if (!is_official_build_) {
359    boot_times_label_ = new views::Label();
360    boot_times_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
361    boot_times_label_->SetColor(kVersionColor);
362    boot_times_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
363    AddChildView(boot_times_label_);
364  }
365
366  if (CrosLibrary::Get()->EnsureLoaded()) {
367    version_loader_.GetVersion(
368        &version_consumer_,
369        NewCallback(this, &BackgroundView::OnVersion),
370        is_official_build_?
371            VersionLoader::VERSION_SHORT_WITH_DATE :
372            VersionLoader::VERSION_FULL);
373    if (!is_official_build_) {
374      boot_times_loader_.GetBootTimes(
375          &boot_times_consumer_,
376          NewCallback(this, &BackgroundView::OnBootTimes));
377    }
378  } else {
379    os_version_label_->SetText(
380        ASCIIToWide(CrosLibrary::Get()->load_error_string()));
381  }
382}
383
384void BackgroundView::InitProgressBar() {
385  std::vector<int> steps;
386  steps.push_back(GetStepId(SELECT_NETWORK));
387#if defined(OFFICIAL_BUILD)
388  steps.push_back(GetStepId(EULA));
389#endif
390  steps.push_back(GetStepId(SIGNIN));
391#if defined(OFFICIAL_BUILD)
392  if (WizardController::IsRegisterScreenDefined())
393    steps.push_back(GetStepId(REGISTRATION));
394#endif
395  steps.push_back(GetStepId(PICTURE));
396  progress_bar_ = new OobeProgressBar(steps);
397  AddChildView(progress_bar_);
398}
399
400void BackgroundView::UpdateWindowType() {
401  std::vector<int> params;
402  params.push_back(did_paint_ ? 1 : 0);
403  WmIpc::instance()->SetWindowType(
404      GTK_WIDGET(GetNativeWindow()),
405      WM_IPC_WINDOW_LOGIN_BACKGROUND,
406      &params);
407}
408
409void BackgroundView::OnVersion(
410    VersionLoader::Handle handle, std::string version) {
411  // TODO(jungshik): Is string concatenation OK here?
412  std::string version_text = l10n_util::GetStringUTF8(IDS_PRODUCT_OS_NAME);
413  version_text += ' ';
414  version_text += l10n_util::GetStringUTF8(IDS_VERSION_FIELD_PREFIX);
415  version_text += ' ';
416  version_text += version;
417
418  // Workaround over incorrect width calculation in old fonts.
419  // TODO(glotov): remove the following line when new fonts are used.
420  version_text += ' ';
421  os_version_label_->SetText(UTF8ToWide(version_text));
422}
423
424void BackgroundView::OnBootTimes(
425    BootTimesLoader::Handle handle, BootTimesLoader::BootTimes boot_times) {
426  const char* kBootTimesNoChromeExec =
427      "Non-firmware boot took %.2f seconds (kernel %.2fs, system %.2fs)";
428  const char* kBootTimesChromeExec =
429      "Non-firmware boot took %.2f seconds "
430      "(kernel %.2fs, system %.2fs, chrome %.2fs)";
431  std::string boot_times_text;
432
433  if (boot_times.chrome > 0) {
434    boot_times_text =
435        base::StringPrintf(
436            kBootTimesChromeExec,
437            boot_times.total,
438            boot_times.pre_startup,
439            boot_times.system,
440            boot_times.chrome);
441  } else {
442    boot_times_text =
443        base::StringPrintf(
444            kBootTimesNoChromeExec,
445            boot_times.total,
446            boot_times.pre_startup,
447            boot_times.system);
448  }
449  // Use UTF8ToWide once this string is localized.
450  boot_times_label_->SetText(ASCIIToWide(boot_times_text));
451}
452
453}  // namespace chromeos
454