Home | History | Annotate | Download | only in wm
      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/base/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   Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
     88   for (Shell::RootWindowList::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::RootWindow* 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::RootWindow *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