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/window_event_dispatcher.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 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 MouseCursorEventFilter* mouse_cursor_filter = 46 Shell::GetInstance()->mouse_cursor_filter(); 47 mouse_cursor_filter->set_mouse_warp_mode( 48 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 = 120 Shell::GetContainer(root_window, kShellWindowId_OverlayContainer); 121 122 widget->Init(params); 123 widget->SetContentsView(this); 124 widget->SetBounds(root_window->GetBoundsInScreen()); 125 widget->GetNativeView()->SetName("PartialScreenshotView"); 126 widget->StackAtTop(); 127 widget->Show(); 128 // Releases the mouse capture to let mouse events come to the view. This 129 // will close the context menu. 130 aura::client::CaptureClient* capture_client = 131 aura::client::GetCaptureClient(root_window); 132 if (capture_client->GetCaptureWindow()) 133 capture_client->ReleaseCapture(capture_client->GetCaptureWindow()); 134 135 overlay_delegate_->RegisterWidget(widget); 136 } 137 138 gfx::Rect PartialScreenshotView::GetScreenshotRect() const { 139 int left = std::min(start_position_.x(), current_position_.x()); 140 int top = std::min(start_position_.y(), current_position_.y()); 141 int width = ::abs(start_position_.x() - current_position_.x()); 142 int height = ::abs(start_position_.y() - current_position_.y()); 143 return gfx::Rect(left, top, width, height); 144 } 145 146 void PartialScreenshotView::OnSelectionStarted(const gfx::Point& position) { 147 start_position_ = position; 148 } 149 150 void PartialScreenshotView::OnSelectionChanged(const gfx::Point& position) { 151 if (is_dragging_ && current_position_ == position) 152 return; 153 current_position_ = position; 154 SchedulePaint(); 155 is_dragging_ = true; 156 } 157 158 void PartialScreenshotView::OnSelectionFinished() { 159 overlay_delegate_->Cancel(); 160 if (!is_dragging_) 161 return; 162 163 is_dragging_ = false; 164 if (screenshot_delegate_) { 165 aura::Window*root_window = 166 GetWidget()->GetNativeWindow()->GetRootWindow(); 167 screenshot_delegate_->HandleTakePartialScreenshot( 168 root_window, 169 gfx::IntersectRects(root_window->bounds(), GetScreenshotRect())); 170 } 171 } 172 173 gfx::NativeCursor PartialScreenshotView::GetCursor( 174 const ui::MouseEvent& event) { 175 // Always use "crosshair" cursor. 176 return ui::kCursorCross; 177 } 178 179 void PartialScreenshotView::OnPaint(gfx::Canvas* canvas) { 180 if (is_dragging_) { 181 // Screenshot area representation: black rectangle with white 182 // rectangle inside. To avoid capturing these rectangles when mouse 183 // release, they should be outside of the actual capturing area. 184 gfx::Rect screenshot_rect = GetScreenshotRect(); 185 screenshot_rect.Inset(-1, -1, -1, -1); 186 canvas->DrawRect(screenshot_rect, SK_ColorWHITE); 187 screenshot_rect.Inset(-1, -1, -1, -1); 188 canvas->DrawRect(screenshot_rect, SK_ColorBLACK); 189 } 190 } 191 192 bool PartialScreenshotView::OnMousePressed(const ui::MouseEvent& event) { 193 // Prevent moving across displays during drag. Capturing a screenshot across 194 // the displays is not supported yet. 195 // TODO(mukai): remove this restriction. 196 MouseCursorEventFilter* mouse_cursor_filter = 197 Shell::GetInstance()->mouse_cursor_filter(); 198 mouse_cursor_filter->set_mouse_warp_mode(MouseCursorEventFilter::WARP_NONE); 199 OnSelectionStarted(event.location()); 200 return true; 201 } 202 203 bool PartialScreenshotView::OnMouseDragged(const ui::MouseEvent& event) { 204 OnSelectionChanged(event.location()); 205 return true; 206 } 207 208 bool PartialScreenshotView::OnMouseWheel(const ui::MouseWheelEvent& event) { 209 // Do nothing but do not propagate events futhermore. 210 return true; 211 } 212 213 void PartialScreenshotView::OnMouseReleased(const ui::MouseEvent& event) { 214 OnSelectionFinished(); 215 } 216 217 void PartialScreenshotView::OnMouseCaptureLost() { 218 is_dragging_ = false; 219 OnSelectionFinished(); 220 } 221 222 void PartialScreenshotView::OnGestureEvent(ui::GestureEvent* event) { 223 switch(event->type()) { 224 case ui::ET_GESTURE_TAP_DOWN: 225 OnSelectionStarted(event->location()); 226 break; 227 case ui::ET_GESTURE_SCROLL_UPDATE: 228 OnSelectionChanged(event->location()); 229 break; 230 case ui::ET_GESTURE_SCROLL_END: 231 case ui::ET_SCROLL_FLING_START: 232 OnSelectionFinished(); 233 break; 234 default: 235 break; 236 } 237 238 event->SetHandled(); 239 } 240 241 } // namespace ash 242