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 28 namespace ash { 29 namespace { 30 31 const int64 kTimeOutMilliseconds = 2000; 32 // Color of the text of the warning message. 33 const SkColor kTextColor = SK_ColorWHITE; 34 // Color of the window background. 35 const SkColor kWindowBackgroundColor = SkColorSetARGB(0xC0, 0x0, 0x0, 0x0); 36 // Radius of the rounded corners of the window. 37 const int kWindowCornerRadius = 2; 38 const int kHorizontalMarginAroundText = 100; 39 const int kVerticalMarginAroundText = 100; 40 41 class 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 100 ExitWarningHandler::ExitWarningHandler() 101 : state_(IDLE), 102 stub_timer_for_test_(false) { 103 } 104 105 ExitWarningHandler::~ExitWarningHandler() { 106 // Note: If a timer is outstanding, it is stopped in its destructor. 107 Hide(); 108 } 109 110 void 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 136 void ExitWarningHandler::TimerAction() { 137 Hide(); 138 if (state_ == WAIT_FOR_DOUBLE_PRESS) 139 state_ = IDLE; 140 } 141 142 void 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 151 void ExitWarningHandler::CancelTimer() { 152 timer_.Stop(); 153 } 154 155 void 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 185 void ExitWarningHandler::Hide() { 186 widget_.reset(); 187 } 188 189 } // namespace ash 190