partial_screenshot_view.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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
24namespace ash {
25
26// A self-owned object to handle the cancel and the finish of current partial
27// screenshot session.
28class 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
82std::vector<PartialScreenshotView*>
83PartialScreenshotView::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
98PartialScreenshotView::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
106PartialScreenshotView::~PartialScreenshotView() {
107  overlay_delegate_ = NULL;
108  screenshot_delegate_ = NULL;
109}
110
111void 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
139gfx::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
147void PartialScreenshotView::OnSelectionStarted(const gfx::Point& position) {
148  start_position_ = position;
149}
150
151void 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
159void 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
174gfx::NativeCursor PartialScreenshotView::GetCursor(
175    const ui::MouseEvent& event) {
176  // Always use "crosshair" cursor.
177  return ui::kCursorCross;
178}
179
180void 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
193bool 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
205bool PartialScreenshotView::OnMouseDragged(const ui::MouseEvent& event) {
206  OnSelectionChanged(event.location());
207  return true;
208}
209
210bool PartialScreenshotView::OnMouseWheel(const ui::MouseWheelEvent& event) {
211  // Do nothing but do not propagate events futhermore.
212  return true;
213}
214
215void PartialScreenshotView::OnMouseReleased(const ui::MouseEvent& event) {
216  OnSelectionFinished();
217}
218
219void PartialScreenshotView::OnMouseCaptureLost() {
220  is_dragging_ = false;
221  OnSelectionFinished();
222}
223
224void 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