// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/wm/partial_screenshot_view.h" #include #include "ash/display/mouse_cursor_event_filter.h" #include "ash/screenshot_delegate.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" #include "ash/wm/overlay_event_filter.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/root_window.h" #include "ui/base/cursor/cursor.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" #include "ui/gfx/rect.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" namespace ash { // A self-owned object to handle the cancel and the finish of current partial // screenshot session. class PartialScreenshotView::OverlayDelegate : public internal::OverlayEventFilter::Delegate, public views::WidgetObserver { public: OverlayDelegate() { Shell::GetInstance()->overlay_filter()->Activate(this); } void RegisterWidget(views::Widget* widget) { widgets_.push_back(widget); widget->AddObserver(this); } // Overridden from OverlayEventFilter::Delegate: virtual void Cancel() OVERRIDE { // Make sure the mouse_warp_mode allows warping. It can be stopped by a // partial screenshot view. internal::MouseCursorEventFilter* mouse_cursor_filter = Shell::GetInstance()->mouse_cursor_filter(); mouse_cursor_filter->set_mouse_warp_mode( internal::MouseCursorEventFilter::WARP_ALWAYS); for (size_t i = 0; i < widgets_.size(); ++i) widgets_[i]->Close(); } virtual bool IsCancelingKeyEvent(ui::KeyEvent* event) OVERRIDE { return event->key_code() == ui::VKEY_ESCAPE; } virtual aura::Window* GetWindow() OVERRIDE { // Just returns NULL because this class does not handle key events in // OverlayEventFilter, except for cancel keys. return NULL; } // Overridden from views::WidgetObserver: virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE { widget->RemoveObserver(this); widgets_.erase(std::remove(widgets_.begin(), widgets_.end(), widget)); if (widgets_.empty()) delete this; } private: virtual ~OverlayDelegate() { Shell::GetInstance()->overlay_filter()->Deactivate(); } std::vector widgets_; DISALLOW_COPY_AND_ASSIGN(OverlayDelegate); }; // static std::vector PartialScreenshotView::StartPartialScreenshot( ScreenshotDelegate* screenshot_delegate) { std::vector views; OverlayDelegate* overlay_delegate = new OverlayDelegate(); Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); for (Shell::RootWindowList::iterator it = root_windows.begin(); it != root_windows.end(); ++it) { PartialScreenshotView* new_view = new PartialScreenshotView( overlay_delegate, screenshot_delegate); new_view->Init(*it); views.push_back(new_view); } return views; } PartialScreenshotView::PartialScreenshotView( PartialScreenshotView::OverlayDelegate* overlay_delegate, ScreenshotDelegate* screenshot_delegate) : is_dragging_(false), overlay_delegate_(overlay_delegate), screenshot_delegate_(screenshot_delegate) { } PartialScreenshotView::~PartialScreenshotView() { overlay_delegate_ = NULL; screenshot_delegate_ = NULL; } void PartialScreenshotView::Init(aura::RootWindow* root_window) { views::Widget* widget = new views::Widget; views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; params.delegate = this; // The partial screenshot rectangle has to be at the real top of // the screen. params.parent = Shell::GetContainer( root_window, internal::kShellWindowId_OverlayContainer); widget->Init(params); widget->SetContentsView(this); widget->SetBounds(root_window->GetBoundsInScreen()); widget->GetNativeView()->SetName("PartialScreenshotView"); widget->StackAtTop(); widget->Show(); // Releases the mouse capture to let mouse events come to the view. This // will close the context menu. aura::client::CaptureClient* capture_client = aura::client::GetCaptureClient(root_window); if (capture_client->GetCaptureWindow()) capture_client->ReleaseCapture(capture_client->GetCaptureWindow()); overlay_delegate_->RegisterWidget(widget); } gfx::Rect PartialScreenshotView::GetScreenshotRect() const { int left = std::min(start_position_.x(), current_position_.x()); int top = std::min(start_position_.y(), current_position_.y()); int width = ::abs(start_position_.x() - current_position_.x()); int height = ::abs(start_position_.y() - current_position_.y()); return gfx::Rect(left, top, width, height); } void PartialScreenshotView::OnSelectionStarted(const gfx::Point& position) { start_position_ = position; } void PartialScreenshotView::OnSelectionChanged(const gfx::Point& position) { if (is_dragging_ && current_position_ == position) return; current_position_ = position; SchedulePaint(); is_dragging_ = true; } void PartialScreenshotView::OnSelectionFinished() { overlay_delegate_->Cancel(); if (!is_dragging_) return; is_dragging_ = false; if (screenshot_delegate_) { aura::RootWindow *root_window = GetWidget()->GetNativeWindow()->GetRootWindow(); screenshot_delegate_->HandleTakePartialScreenshot( root_window, gfx::IntersectRects(root_window->bounds(), GetScreenshotRect())); } } gfx::NativeCursor PartialScreenshotView::GetCursor( const ui::MouseEvent& event) { // Always use "crosshair" cursor. return ui::kCursorCross; } void PartialScreenshotView::OnPaint(gfx::Canvas* canvas) { if (is_dragging_) { // Screenshot area representation: black rectangle with white // rectangle inside. To avoid capturing these rectangles when mouse // release, they should be outside of the actual capturing area. gfx::Rect screenshot_rect = GetScreenshotRect(); screenshot_rect.Inset(-1, -1, -1, -1); canvas->DrawRect(screenshot_rect, SK_ColorWHITE); screenshot_rect.Inset(-1, -1, -1, -1); canvas->DrawRect(screenshot_rect, SK_ColorBLACK); } } bool PartialScreenshotView::OnMousePressed(const ui::MouseEvent& event) { // Prevent moving across displays during drag. Capturing a screenshot across // the displays is not supported yet. // TODO(mukai): remove this restriction. internal::MouseCursorEventFilter* mouse_cursor_filter = Shell::GetInstance()->mouse_cursor_filter(); mouse_cursor_filter->set_mouse_warp_mode( internal::MouseCursorEventFilter::WARP_NONE); OnSelectionStarted(event.location()); return true; } bool PartialScreenshotView::OnMouseDragged(const ui::MouseEvent& event) { OnSelectionChanged(event.location()); return true; } bool PartialScreenshotView::OnMouseWheel(const ui::MouseWheelEvent& event) { // Do nothing but do not propagate events futhermore. return true; } void PartialScreenshotView::OnMouseReleased(const ui::MouseEvent& event) { OnSelectionFinished(); } void PartialScreenshotView::OnMouseCaptureLost() { is_dragging_ = false; OnSelectionFinished(); } void PartialScreenshotView::OnGestureEvent(ui::GestureEvent* event) { switch(event->type()) { case ui::ET_GESTURE_TAP_DOWN: OnSelectionStarted(event->location()); break; case ui::ET_GESTURE_SCROLL_UPDATE: OnSelectionChanged(event->location()); break; case ui::ET_GESTURE_SCROLL_END: case ui::ET_SCROLL_FLING_START: OnSelectionFinished(); break; default: break; } event->SetHandled(); } } // namespace ash