1// Copyright 2013 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 "ash/accelerators/exit_warning_handler.h"
6
7#include "ash/metrics/user_metrics_recorder.h"
8#include "ash/shell.h"
9#include "ash/shell_delegate.h"
10#include "ash/shell_window_ids.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/time/time.h"
13#include "base/timer/timer.h"
14#include "grit/ash_strings.h"
15#include "ui/accessibility/ax_view_state.h"
16#include "ui/aura/window.h"
17#include "ui/base/l10n/l10n_util.h"
18#include "ui/base/resource/resource_bundle.h"
19#include "ui/gfx/canvas.h"
20#include "ui/gfx/font_list.h"
21#include "ui/gfx/text_utils.h"
22#include "ui/views/controls/label.h"
23#include "ui/views/layout/fill_layout.h"
24#include "ui/views/view.h"
25#include "ui/views/widget/widget.h"
26#include "ui/views/widget/widget_delegate.h"
27
28namespace ash {
29namespace {
30
31const int64 kTimeOutMilliseconds = 2000;
32// Color of the text of the warning message.
33const SkColor kTextColor = SK_ColorWHITE;
34// Color of the window background.
35const SkColor kWindowBackgroundColor = SkColorSetARGB(0xC0, 0x0, 0x0, 0x0);
36// Radius of the rounded corners of the window.
37const int kWindowCornerRadius = 2;
38const int kHorizontalMarginAroundText = 100;
39const int kVerticalMarginAroundText = 100;
40
41class ExitWarningWidgetDelegateView : public views::WidgetDelegateView {
42 public:
43  ExitWarningWidgetDelegateView() : text_width_(0), width_(0), height_(0) {
44#ifdef OS_CHROMEOS
45    text_ = l10n_util::GetStringUTF16(IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT);
46    accessible_name_ = l10n_util::GetStringUTF16(
47        IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT_ACCESSIBLE);
48#else
49    text_ = l10n_util::GetStringUTF16(IDS_ASH_EXIT_WARNING_POPUP_TEXT);
50    accessible_name_ =
51        l10n_util::GetStringUTF16(IDS_ASH_EXIT_WARNING_POPUP_TEXT_ACCESSIBLE);
52#endif
53    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
54    const gfx::FontList& font_list =
55        rb.GetFontList(ui::ResourceBundle::LargeFont);
56    text_width_ = gfx::GetStringWidth(text_, font_list);
57    width_ = text_width_ + kHorizontalMarginAroundText;
58    height_ = font_list.GetHeight() + kVerticalMarginAroundText;
59    views::Label* label = new views::Label();
60    label->SetText(text_);
61    label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
62    label->SetFontList(font_list);
63    label->SetEnabledColor(kTextColor);
64    label->SetDisabledColor(kTextColor);
65    label->SetAutoColorReadabilityEnabled(false);
66    label->SetSubpixelRenderingEnabled(false);
67    AddChildView(label);
68    SetLayoutManager(new views::FillLayout);
69  }
70
71  virtual gfx::Size GetPreferredSize() const OVERRIDE {
72    return gfx::Size(width_, height_);
73  }
74
75  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
76    SkPaint paint;
77    paint.setStyle(SkPaint::kFill_Style);
78    paint.setColor(kWindowBackgroundColor);
79    canvas->DrawRoundRect(GetLocalBounds(), kWindowCornerRadius, paint);
80    views::WidgetDelegateView::OnPaint(canvas);
81  }
82
83  virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
84    state->name = accessible_name_;
85    state->role = ui::AX_ROLE_ALERT;
86  }
87
88 private:
89  base::string16 text_;
90  base::string16 accessible_name_;
91  int text_width_;
92  int width_;
93  int height_;
94
95  DISALLOW_COPY_AND_ASSIGN(ExitWarningWidgetDelegateView);
96};
97
98}  // namespace
99
100ExitWarningHandler::ExitWarningHandler()
101    : state_(IDLE),
102      stub_timer_for_test_(false) {
103}
104
105ExitWarningHandler::~ExitWarningHandler() {
106  // Note: If a timer is outstanding, it is stopped in its destructor.
107  Hide();
108}
109
110void ExitWarningHandler::HandleAccelerator() {
111  ShellDelegate* shell_delegate = Shell::GetInstance()->delegate();
112  switch (state_) {
113    case IDLE:
114      state_ = WAIT_FOR_DOUBLE_PRESS;
115      Show();
116      StartTimer();
117      Shell::GetInstance()->
118          metrics()->RecordUserMetricsAction(UMA_ACCEL_EXIT_FIRST_Q);
119      break;
120    case WAIT_FOR_DOUBLE_PRESS:
121      state_ = EXITING;
122      CancelTimer();
123      Hide();
124      Shell::GetInstance()->
125          metrics()->RecordUserMetricsAction(UMA_ACCEL_EXIT_SECOND_Q);
126      shell_delegate->Exit();
127      break;
128    case EXITING:
129      break;
130    default:
131      NOTREACHED();
132      break;
133  }
134}
135
136void ExitWarningHandler::TimerAction() {
137  Hide();
138  if (state_ == WAIT_FOR_DOUBLE_PRESS)
139    state_ = IDLE;
140}
141
142void ExitWarningHandler::StartTimer() {
143  if (stub_timer_for_test_)
144    return;
145  timer_.Start(FROM_HERE,
146               base::TimeDelta::FromMilliseconds(kTimeOutMilliseconds),
147               this,
148               &ExitWarningHandler::TimerAction);
149}
150
151void ExitWarningHandler::CancelTimer() {
152  timer_.Stop();
153}
154
155void ExitWarningHandler::Show() {
156  if (widget_)
157    return;
158  aura::Window* root_window = Shell::GetTargetRootWindow();
159  ExitWarningWidgetDelegateView* delegate = new ExitWarningWidgetDelegateView;
160  gfx::Size rs = root_window->bounds().size();
161  gfx::Size ps = delegate->GetPreferredSize();
162  gfx::Rect bounds((rs.width() - ps.width()) / 2,
163                   (rs.height() - ps.height()) / 3,
164                   ps.width(), ps.height());
165  views::Widget::InitParams params;
166  params.type = views::Widget::InitParams::TYPE_POPUP;
167  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
168  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
169  params.accept_events = false;
170  params.keep_on_top = true;
171  params.remove_standard_frame = true;
172  params.delegate = delegate;
173  params.bounds = bounds;
174  params.parent =
175      Shell::GetContainer(root_window, kShellWindowId_SettingBubbleContainer);
176  widget_.reset(new views::Widget);
177  widget_->Init(params);
178  widget_->SetContentsView(delegate);
179  widget_->GetNativeView()->SetName("ExitWarningWindow");
180  widget_->Show();
181
182  delegate->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
183}
184
185void ExitWarningHandler::Hide() {
186  widget_.reset();
187}
188
189}  // namespace ash
190