• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
OverlayDelegate()32   OverlayDelegate() {
33     Shell::GetInstance()->overlay_filter()->Activate(this);
34   }
35 
RegisterWidget(views::Widget * widget)36   void RegisterWidget(views::Widget* widget) {
37     widgets_.push_back(widget);
38     widget->AddObserver(this);
39   }
40 
41   // Overridden from OverlayEventFilter::Delegate:
Cancel()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 
IsCancelingKeyEvent(ui::KeyEvent * event)53   virtual bool IsCancelingKeyEvent(ui::KeyEvent* event) OVERRIDE {
54     return event->key_code() == ui::VKEY_ESCAPE;
55   }
56 
GetWindow()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:
OnWidgetDestroying(views::Widget * widget)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:
~OverlayDelegate()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*>
StartPartialScreenshot(ScreenshotDelegate * screenshot_delegate)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 
PartialScreenshotView(PartialScreenshotView::OverlayDelegate * overlay_delegate,ScreenshotDelegate * screenshot_delegate)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 
~PartialScreenshotView()106 PartialScreenshotView::~PartialScreenshotView() {
107   overlay_delegate_ = NULL;
108   screenshot_delegate_ = NULL;
109 }
110 
Init(aura::Window * root_window)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 
GetScreenshotRect() const139 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 
OnSelectionStarted(const gfx::Point & position)147 void PartialScreenshotView::OnSelectionStarted(const gfx::Point& position) {
148   start_position_ = position;
149 }
150 
OnSelectionChanged(const gfx::Point & position)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 
OnSelectionFinished()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 
GetCursor(const ui::MouseEvent & event)174 gfx::NativeCursor PartialScreenshotView::GetCursor(
175     const ui::MouseEvent& event) {
176   // Always use "crosshair" cursor.
177   return ui::kCursorCross;
178 }
179 
OnPaint(gfx::Canvas * canvas)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 
OnMousePressed(const ui::MouseEvent & event)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 
OnMouseDragged(const ui::MouseEvent & event)205 bool PartialScreenshotView::OnMouseDragged(const ui::MouseEvent& event) {
206   OnSelectionChanged(event.location());
207   return true;
208 }
209 
OnMouseWheel(const ui::MouseWheelEvent & event)210 bool PartialScreenshotView::OnMouseWheel(const ui::MouseWheelEvent& event) {
211   // Do nothing but do not propagate events futhermore.
212   return true;
213 }
214 
OnMouseReleased(const ui::MouseEvent & event)215 void PartialScreenshotView::OnMouseReleased(const ui::MouseEvent& event) {
216   OnSelectionFinished();
217 }
218 
OnMouseCaptureLost()219 void PartialScreenshotView::OnMouseCaptureLost() {
220   is_dragging_ = false;
221   OnSelectionFinished();
222 }
223 
OnGestureEvent(ui::GestureEvent * event)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