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/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