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 "ash/wm/partial_screenshot_view.h" 6 7 #include <algorithm> 8 9 #include "ash/display/mouse_cursor_event_filter.h" 10 #include "ash/screenshot_delegate.h" 11 #include "ash/shell.h" 12 #include "ash/shell_window_ids.h" 13 #include "ash/wm/overlay_event_filter.h" 14 #include "ui/aura/client/capture_client.h" 15 #include "ui/aura/root_window.h" 16 #include "ui/base/cursor/cursor.h" 17 #include "ui/events/event.h" 18 #include "ui/gfx/canvas.h" 19 #include "ui/gfx/rect.h" 20 #include "ui/views/view.h" 21 #include "ui/views/widget/widget.h" 22 #include "ui/views/widget/widget_observer.h" 23 24 namespace ash { 25 26 // A self-owned object to handle the cancel and the finish of current partial 27 // screenshot session. 28 class PartialScreenshotView::OverlayDelegate 29 : public internal::OverlayEventFilter::Delegate, 30 public views::WidgetObserver { 31 public: 32 OverlayDelegate() { 33 Shell::GetInstance()->overlay_filter()->Activate(this); 34 } 35 36 void RegisterWidget(views::Widget* widget) { 37 widgets_.push_back(widget); 38 widget->AddObserver(this); 39 } 40 41 // Overridden from OverlayEventFilter::Delegate: 42 virtual void Cancel() OVERRIDE { 43 // Make sure the mouse_warp_mode allows warping. It can be stopped by a 44 // partial screenshot view. 45 internal::MouseCursorEventFilter* mouse_cursor_filter = 46 Shell::GetInstance()->mouse_cursor_filter(); 47 mouse_cursor_filter->set_mouse_warp_mode( 48 internal::MouseCursorEventFilter::WARP_ALWAYS); 49 for (size_t i = 0; i < widgets_.size(); ++i) 50 widgets_[i]->Close(); 51 } 52 53 virtual bool IsCancelingKeyEvent(ui::KeyEvent* event) OVERRIDE { 54 return event->key_code() == ui::VKEY_ESCAPE; 55 } 56 57 virtual aura::Window* GetWindow() OVERRIDE { 58 // Just returns NULL because this class does not handle key events in 59 // OverlayEventFilter, except for cancel keys. 60 return NULL; 61 } 62 63 // Overridden from views::WidgetObserver: 64 virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE { 65 widget->RemoveObserver(this); 66 widgets_.erase(std::remove(widgets_.begin(), widgets_.end(), widget)); 67 if (widgets_.empty()) 68 delete this; 69 } 70 71 private: 72 virtual ~OverlayDelegate() { 73 Shell::GetInstance()->overlay_filter()->Deactivate(); 74 } 75 76 std::vector<views::Widget*> widgets_; 77 78 DISALLOW_COPY_AND_ASSIGN(OverlayDelegate); 79 }; 80 81 // static 82 std::vector<PartialScreenshotView*> 83 PartialScreenshotView::StartPartialScreenshot( 84 ScreenshotDelegate* screenshot_delegate) { 85 std::vector<PartialScreenshotView*> views; 86 OverlayDelegate* overlay_delegate = new OverlayDelegate(); 87 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 88 for (aura::Window::Windows::iterator it = root_windows.begin(); 89 it != root_windows.end(); ++it) { 90 PartialScreenshotView* new_view = new PartialScreenshotView( 91 overlay_delegate, screenshot_delegate); 92 new_view->Init(*it); 93 views.push_back(new_view); 94 } 95 return views; 96 } 97 98 PartialScreenshotView::PartialScreenshotView( 99 PartialScreenshotView::OverlayDelegate* overlay_delegate, 100 ScreenshotDelegate* screenshot_delegate) 101 : is_dragging_(false), 102 overlay_delegate_(overlay_delegate), 103 screenshot_delegate_(screenshot_delegate) { 104 } 105 106 PartialScreenshotView::~PartialScreenshotView() { 107 overlay_delegate_ = NULL; 108 screenshot_delegate_ = NULL; 109 } 110 111 void PartialScreenshotView::Init(aura::Window* root_window) { 112 views::Widget* widget = new views::Widget; 113 views::Widget::InitParams params( 114 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 115 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 116 params.delegate = this; 117 // The partial screenshot rectangle has to be at the real top of 118 // the screen. 119 params.parent = Shell::GetContainer( 120 root_window, 121 internal::kShellWindowId_OverlayContainer); 122 123 widget->Init(params); 124 widget->SetContentsView(this); 125 widget->SetBounds(root_window->GetBoundsInScreen()); 126 widget->GetNativeView()->SetName("PartialScreenshotView"); 127 widget->StackAtTop(); 128 widget->Show(); 129 // Releases the mouse capture to let mouse events come to the view. This 130 // will close the context menu. 131 aura::client::CaptureClient* capture_client = 132 aura::client::GetCaptureClient(root_window); 133 if (capture_client->GetCaptureWindow()) 134 capture_client->ReleaseCapture(capture_client->GetCaptureWindow()); 135 136 overlay_delegate_->RegisterWidget(widget); 137 } 138 139 gfx::Rect PartialScreenshotView::GetScreenshotRect() const { 140 int left = std::min(start_position_.x(), current_position_.x()); 141 int top = std::min(start_position_.y(), current_position_.y()); 142 int width = ::abs(start_position_.x() - current_position_.x()); 143 int height = ::abs(start_position_.y() - current_position_.y()); 144 return gfx::Rect(left, top, width, height); 145 } 146 147 void PartialScreenshotView::OnSelectionStarted(const gfx::Point& position) { 148 start_position_ = position; 149 } 150 151 void PartialScreenshotView::OnSelectionChanged(const gfx::Point& position) { 152 if (is_dragging_ && current_position_ == position) 153 return; 154 current_position_ = position; 155 SchedulePaint(); 156 is_dragging_ = true; 157 } 158 159 void PartialScreenshotView::OnSelectionFinished() { 160 overlay_delegate_->Cancel(); 161 if (!is_dragging_) 162 return; 163 164 is_dragging_ = false; 165 if (screenshot_delegate_) { 166 aura::Window*root_window = 167 GetWidget()->GetNativeWindow()->GetRootWindow(); 168 screenshot_delegate_->HandleTakePartialScreenshot( 169 root_window, 170 gfx::IntersectRects(root_window->bounds(), GetScreenshotRect())); 171 } 172 } 173 174 gfx::NativeCursor PartialScreenshotView::GetCursor( 175 const ui::MouseEvent& event) { 176 // Always use "crosshair" cursor. 177 return ui::kCursorCross; 178 } 179 180 void PartialScreenshotView::OnPaint(gfx::Canvas* canvas) { 181 if (is_dragging_) { 182 // Screenshot area representation: black rectangle with white 183 // rectangle inside. To avoid capturing these rectangles when mouse 184 // release, they should be outside of the actual capturing area. 185 gfx::Rect screenshot_rect = GetScreenshotRect(); 186 screenshot_rect.Inset(-1, -1, -1, -1); 187 canvas->DrawRect(screenshot_rect, SK_ColorWHITE); 188 screenshot_rect.Inset(-1, -1, -1, -1); 189 canvas->DrawRect(screenshot_rect, SK_ColorBLACK); 190 } 191 } 192 193 bool PartialScreenshotView::OnMousePressed(const ui::MouseEvent& event) { 194 // Prevent moving across displays during drag. Capturing a screenshot across 195 // the displays is not supported yet. 196 // TODO(mukai): remove this restriction. 197 internal::MouseCursorEventFilter* mouse_cursor_filter = 198 Shell::GetInstance()->mouse_cursor_filter(); 199 mouse_cursor_filter->set_mouse_warp_mode( 200 internal::MouseCursorEventFilter::WARP_NONE); 201 OnSelectionStarted(event.location()); 202 return true; 203 } 204 205 bool PartialScreenshotView::OnMouseDragged(const ui::MouseEvent& event) { 206 OnSelectionChanged(event.location()); 207 return true; 208 } 209 210 bool PartialScreenshotView::OnMouseWheel(const ui::MouseWheelEvent& event) { 211 // Do nothing but do not propagate events futhermore. 212 return true; 213 } 214 215 void PartialScreenshotView::OnMouseReleased(const ui::MouseEvent& event) { 216 OnSelectionFinished(); 217 } 218 219 void PartialScreenshotView::OnMouseCaptureLost() { 220 is_dragging_ = false; 221 OnSelectionFinished(); 222 } 223 224 void PartialScreenshotView::OnGestureEvent(ui::GestureEvent* event) { 225 switch(event->type()) { 226 case ui::ET_GESTURE_TAP_DOWN: 227 OnSelectionStarted(event->location()); 228 break; 229 case ui::ET_GESTURE_SCROLL_UPDATE: 230 OnSelectionChanged(event->location()); 231 break; 232 case ui::ET_GESTURE_SCROLL_END: 233 case ui::ET_SCROLL_FLING_START: 234 OnSelectionFinished(); 235 break; 236 default: 237 break; 238 } 239 240 event->SetHandled(); 241 } 242 243 } // namespace ash 244