Update parsing of dumpsys batterystats
[chromium-blink-merge.git] / ash / drag_drop / drag_drop_controller.cc
blob2ff526fe7681f6452321be3ff29e974547d38d28
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.
5 #include "ash/drag_drop/drag_drop_controller.h"
7 #include "ash/drag_drop/drag_drop_tracker.h"
8 #include "ash/drag_drop/drag_image_view.h"
9 #include "ash/shell.h"
10 #include "ash/wm/coordinate_conversion.h"
11 #include "base/bind.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "ui/aura/client/capture_client.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_delegate.h"
18 #include "ui/aura/window_event_dispatcher.h"
19 #include "ui/base/dragdrop/drag_drop_types.h"
20 #include "ui/base/dragdrop/os_exchange_data.h"
21 #include "ui/base/hit_test.h"
22 #include "ui/events/event.h"
23 #include "ui/events/event_utils.h"
24 #include "ui/gfx/animation/linear_animation.h"
25 #include "ui/gfx/path.h"
26 #include "ui/gfx/point.h"
27 #include "ui/gfx/rect.h"
28 #include "ui/gfx/rect_conversions.h"
29 #include "ui/views/views_delegate.h"
30 #include "ui/views/widget/native_widget_aura.h"
31 #include "ui/wm/public/drag_drop_delegate.h"
33 namespace ash {
34 namespace {
36 // The duration of the drag cancel animation in millisecond.
37 const int kCancelAnimationDuration = 250;
38 const int kTouchCancelAnimationDuration = 20;
39 // The frame rate of the drag cancel animation in hertz.
40 const int kCancelAnimationFrameRate = 60;
42 // For touch initiated dragging, we scale and shift drag image by the following:
43 static const float kTouchDragImageScale = 1.2f;
44 static const int kTouchDragImageVerticalOffset = -25;
46 // Adjusts the drag image bounds such that the new bounds are scaled by |scale|
47 // and translated by the |drag_image_offset| and and additional
48 // |vertical_offset|.
49 gfx::Rect AdjustDragImageBoundsForScaleAndOffset(
50 const gfx::Rect& drag_image_bounds,
51 int vertical_offset,
52 float scale,
53 gfx::Vector2d* drag_image_offset) {
54 gfx::PointF final_origin = drag_image_bounds.origin();
55 gfx::SizeF final_size = drag_image_bounds.size();
56 final_size.Scale(scale);
57 drag_image_offset->set_x(drag_image_offset->x() * scale);
58 drag_image_offset->set_y(drag_image_offset->y() * scale);
59 float total_x_offset = drag_image_offset->x();
60 float total_y_offset = drag_image_offset->y() - vertical_offset;
61 final_origin.Offset(-total_x_offset, -total_y_offset);
62 return gfx::ToEnclosingRect(gfx::RectF(final_origin, final_size));
65 void DispatchGestureEndToWindow(aura::Window* window) {
66 if (window && window->delegate()) {
67 ui::GestureEvent gesture_end(
71 ui::EventTimeForNow(),
72 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0));
73 window->delegate()->OnGestureEvent(&gesture_end);
76 } // namespace
78 class DragDropTrackerDelegate : public aura::WindowDelegate {
79 public:
80 explicit DragDropTrackerDelegate(DragDropController* controller)
81 : drag_drop_controller_(controller) {}
82 virtual ~DragDropTrackerDelegate() {}
84 // Overridden from WindowDelegate:
85 virtual gfx::Size GetMinimumSize() const OVERRIDE {
86 return gfx::Size();
89 virtual gfx::Size GetMaximumSize() const OVERRIDE {
90 return gfx::Size();
93 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
94 const gfx::Rect& new_bounds) OVERRIDE {}
95 virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
96 return gfx::kNullCursor;
98 virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE {
99 return HTCAPTION;
101 virtual bool ShouldDescendIntoChildForEventHandling(
102 aura::Window* child,
103 const gfx::Point& location) OVERRIDE {
104 return true;
106 virtual bool CanFocus() OVERRIDE { return true; }
107 virtual void OnCaptureLost() OVERRIDE {
108 if (drag_drop_controller_->IsDragDropInProgress())
109 drag_drop_controller_->DragCancel();
111 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
113 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
114 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {}
115 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {}
116 virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {}
117 virtual bool HasHitTestMask() const OVERRIDE {
118 return true;
120 virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {
121 DCHECK(mask->isEmpty());
124 private:
125 DragDropController* drag_drop_controller_;
127 DISALLOW_COPY_AND_ASSIGN(DragDropTrackerDelegate);
130 ////////////////////////////////////////////////////////////////////////////////
131 // DragDropController, public:
133 DragDropController::DragDropController()
134 : drag_data_(NULL),
135 drag_operation_(0),
136 drag_window_(NULL),
137 drag_source_window_(NULL),
138 should_block_during_drag_drop_(true),
139 drag_drop_window_delegate_(new DragDropTrackerDelegate(this)),
140 current_drag_event_source_(ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE),
141 weak_factory_(this) {
142 Shell::GetInstance()->PrependPreTargetHandler(this);
145 DragDropController::~DragDropController() {
146 Shell::GetInstance()->RemovePreTargetHandler(this);
147 Cleanup();
148 if (cancel_animation_)
149 cancel_animation_->End();
150 if (drag_image_)
151 drag_image_.reset();
154 int DragDropController::StartDragAndDrop(
155 const ui::OSExchangeData& data,
156 aura::Window* root_window,
157 aura::Window* source_window,
158 const gfx::Point& root_location,
159 int operation,
160 ui::DragDropTypes::DragEventSource source) {
161 if (IsDragDropInProgress())
162 return 0;
164 const ui::OSExchangeData::Provider* provider = &data.provider();
165 // We do not support touch drag/drop without a drag image.
166 if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH &&
167 provider->GetDragImage().size().IsEmpty())
168 return 0;
170 current_drag_event_source_ = source;
171 DragDropTracker* tracker =
172 new DragDropTracker(root_window, drag_drop_window_delegate_.get());
173 if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
174 // We need to transfer the current gesture sequence and the GR's touch event
175 // queue to the |drag_drop_tracker_|'s capture window so that when it takes
176 // capture, it still gets a valid gesture state.
177 ui::GestureRecognizer::Get()->TransferEventsTo(source_window,
178 tracker->capture_window());
179 // We also send a gesture end to the source window so it can clear state.
180 // TODO(varunjain): Remove this whole block when gesture sequence
181 // transferring is properly done in the GR (http://crbug.com/160558)
182 DispatchGestureEndToWindow(source_window);
184 tracker->TakeCapture();
185 drag_drop_tracker_.reset(tracker);
186 drag_source_window_ = source_window;
187 if (drag_source_window_)
188 drag_source_window_->AddObserver(this);
189 pending_long_tap_.reset();
191 drag_data_ = &data;
192 drag_operation_ = operation;
194 float drag_image_scale = 1;
195 int drag_image_vertical_offset = 0;
196 if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
197 drag_image_scale = kTouchDragImageScale;
198 drag_image_vertical_offset = kTouchDragImageVerticalOffset;
200 gfx::Point start_location = root_location;
201 ash::wm::ConvertPointToScreen(root_window, &start_location);
202 drag_image_final_bounds_for_cancel_animation_ = gfx::Rect(
203 start_location - provider->GetDragImageOffset(),
204 provider->GetDragImage().size());
205 drag_image_.reset(new DragImageView(source_window->GetRootWindow(), source));
206 drag_image_->SetImage(provider->GetDragImage());
207 drag_image_offset_ = provider->GetDragImageOffset();
208 gfx::Rect drag_image_bounds(start_location, drag_image_->GetPreferredSize());
209 drag_image_bounds = AdjustDragImageBoundsForScaleAndOffset(drag_image_bounds,
210 drag_image_vertical_offset, drag_image_scale, &drag_image_offset_);
211 drag_image_->SetBoundsInScreen(drag_image_bounds);
212 drag_image_->SetWidgetVisible(true);
213 if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) {
214 drag_image_->SetTouchDragOperationHintPosition(gfx::Point(
215 drag_image_offset_.x(),
216 drag_image_offset_.y() + drag_image_vertical_offset));
219 drag_window_ = NULL;
221 // Ends cancel animation if it's in progress.
222 if (cancel_animation_)
223 cancel_animation_->End();
225 if (should_block_during_drag_drop_) {
226 base::RunLoop run_loop;
227 quit_closure_ = run_loop.QuitClosure();
228 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
229 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
230 run_loop.Run();
233 if (!cancel_animation_.get() || !cancel_animation_->is_animating() ||
234 !pending_long_tap_.get()) {
235 // If drag cancel animation is running, this cleanup is done when the
236 // animation completes.
237 if (drag_source_window_)
238 drag_source_window_->RemoveObserver(this);
239 drag_source_window_ = NULL;
242 return drag_operation_;
245 void DragDropController::DragUpdate(aura::Window* target,
246 const ui::LocatedEvent& event) {
247 int op = ui::DragDropTypes::DRAG_NONE;
248 if (target != drag_window_) {
249 if (drag_window_) {
250 aura::client::DragDropDelegate* delegate =
251 aura::client::GetDragDropDelegate(drag_window_);
252 if (delegate)
253 delegate->OnDragExited();
254 if (drag_window_ != drag_source_window_)
255 drag_window_->RemoveObserver(this);
257 drag_window_ = target;
258 // We are already an observer of |drag_source_window_| so no need to add.
259 if (drag_window_ != drag_source_window_)
260 drag_window_->AddObserver(this);
261 aura::client::DragDropDelegate* delegate =
262 aura::client::GetDragDropDelegate(drag_window_);
263 if (delegate) {
264 ui::DropTargetEvent e(*drag_data_,
265 event.location(),
266 event.root_location(),
267 drag_operation_);
268 e.set_flags(event.flags());
269 delegate->OnDragEntered(e);
271 } else {
272 aura::client::DragDropDelegate* delegate =
273 aura::client::GetDragDropDelegate(drag_window_);
274 if (delegate) {
275 ui::DropTargetEvent e(*drag_data_,
276 event.location(),
277 event.root_location(),
278 drag_operation_);
279 e.set_flags(event.flags());
280 op = delegate->OnDragUpdated(e);
281 gfx::NativeCursor cursor = ui::kCursorNoDrop;
282 if (op & ui::DragDropTypes::DRAG_COPY)
283 cursor = ui::kCursorCopy;
284 else if (op & ui::DragDropTypes::DRAG_LINK)
285 cursor = ui::kCursorAlias;
286 else if (op & ui::DragDropTypes::DRAG_MOVE)
287 cursor = ui::kCursorGrabbing;
288 ash::Shell::GetInstance()->cursor_manager()->SetCursor(cursor);
292 DCHECK(drag_image_.get());
293 if (drag_image_->visible()) {
294 gfx::Point root_location_in_screen = event.root_location();
295 ash::wm::ConvertPointToScreen(target->GetRootWindow(),
296 &root_location_in_screen);
297 drag_image_->SetScreenPosition(
298 root_location_in_screen - drag_image_offset_);
299 drag_image_->SetTouchDragOperation(op);
303 void DragDropController::Drop(aura::Window* target,
304 const ui::LocatedEvent& event) {
305 ash::Shell::GetInstance()->cursor_manager()->SetCursor(ui::kCursorPointer);
307 // We must guarantee that a target gets a OnDragEntered before Drop. WebKit
308 // depends on not getting a Drop without DragEnter. This behavior is
309 // consistent with drag/drop on other platforms.
310 if (target != drag_window_)
311 DragUpdate(target, event);
312 DCHECK(target == drag_window_);
314 aura::client::DragDropDelegate* delegate =
315 aura::client::GetDragDropDelegate(target);
316 if (delegate) {
317 ui::DropTargetEvent e(
318 *drag_data_, event.location(), event.root_location(), drag_operation_);
319 e.set_flags(event.flags());
320 drag_operation_ = delegate->OnPerformDrop(e);
321 if (drag_operation_ == 0)
322 StartCanceledAnimation(kCancelAnimationDuration);
323 else
324 drag_image_.reset();
325 } else {
326 drag_image_.reset();
329 Cleanup();
330 if (should_block_during_drag_drop_)
331 quit_closure_.Run();
334 void DragDropController::DragCancel() {
335 DoDragCancel(kCancelAnimationDuration);
338 bool DragDropController::IsDragDropInProgress() {
339 return !!drag_drop_tracker_.get();
342 void DragDropController::OnKeyEvent(ui::KeyEvent* event) {
343 if (IsDragDropInProgress() && event->key_code() == ui::VKEY_ESCAPE) {
344 DragCancel();
345 event->StopPropagation();
349 void DragDropController::OnMouseEvent(ui::MouseEvent* event) {
350 if (!IsDragDropInProgress())
351 return;
353 // If current drag session was not started by mouse, dont process this mouse
354 // event, but consume it so it does not interfere with current drag session.
355 if (current_drag_event_source_ !=
356 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE) {
357 event->StopPropagation();
358 return;
361 aura::Window* translated_target = drag_drop_tracker_->GetTarget(*event);
362 if (!translated_target) {
363 DragCancel();
364 event->StopPropagation();
365 return;
367 scoped_ptr<ui::LocatedEvent> translated_event(
368 drag_drop_tracker_->ConvertEvent(translated_target, *event));
369 switch (translated_event->type()) {
370 case ui::ET_MOUSE_DRAGGED:
371 DragUpdate(translated_target, *translated_event.get());
372 break;
373 case ui::ET_MOUSE_RELEASED:
374 Drop(translated_target, *translated_event.get());
375 break;
376 default:
377 // We could also reach here because RootWindow may sometimes generate a
378 // bunch of fake mouse events
379 // (aura::RootWindow::PostMouseMoveEventAfterWindowChange).
380 break;
382 event->StopPropagation();
385 void DragDropController::OnTouchEvent(ui::TouchEvent* event) {
386 if (!IsDragDropInProgress())
387 return;
389 // If current drag session was not started by touch, dont process this touch
390 // event, but consume it so it does not interfere with current drag session.
391 if (current_drag_event_source_ != ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)
392 event->StopPropagation();
394 if (event->handled())
395 return;
397 if (event->type() == ui::ET_TOUCH_CANCELLED)
398 DragCancel();
401 void DragDropController::OnGestureEvent(ui::GestureEvent* event) {
402 if (!IsDragDropInProgress())
403 return;
405 // No one else should handle gesture events when in drag drop. Note that it is
406 // not enough to just set ER_HANDLED because the dispatcher only stops
407 // dispatching when the event has ER_CONSUMED. If we just set ER_HANDLED, the
408 // event will still be dispatched to other handlers and we depend on
409 // individual handlers' kindness to not touch events marked ER_HANDLED (not
410 // all handlers are so kind and may cause bugs like crbug.com/236493).
411 event->StopPropagation();
413 // If current drag session was not started by touch, dont process this event.
414 if (current_drag_event_source_ != ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)
415 return;
417 // Apply kTouchDragImageVerticalOffset to the location.
418 ui::GestureEvent touch_offset_event(*event,
419 static_cast<aura::Window*>(NULL),
420 static_cast<aura::Window*>(NULL));
421 gfx::Point touch_offset_location = touch_offset_event.location();
422 gfx::Point touch_offset_root_location = touch_offset_event.root_location();
423 touch_offset_location.Offset(0, kTouchDragImageVerticalOffset);
424 touch_offset_root_location.Offset(0, kTouchDragImageVerticalOffset);
425 touch_offset_event.set_location(touch_offset_location);
426 touch_offset_event.set_root_location(touch_offset_root_location);
428 aura::Window* translated_target =
429 drag_drop_tracker_->GetTarget(touch_offset_event);
430 if (!translated_target) {
431 DragCancel();
432 event->SetHandled();
433 return;
435 scoped_ptr<ui::LocatedEvent> translated_event(
436 drag_drop_tracker_->ConvertEvent(translated_target, touch_offset_event));
438 switch (event->type()) {
439 case ui::ET_GESTURE_SCROLL_UPDATE:
440 DragUpdate(translated_target, *translated_event.get());
441 break;
442 case ui::ET_GESTURE_SCROLL_END:
443 Drop(translated_target, *translated_event.get());
444 break;
445 case ui::ET_SCROLL_FLING_START:
446 case ui::ET_GESTURE_LONG_TAP:
447 // Ideally we would want to just forward this long tap event to the
448 // |drag_source_window_|. However, webkit does not accept events while a
449 // drag drop is still in progress. The drag drop ends only when the nested
450 // message loop ends. Due to this stupidity, we have to defer forwarding
451 // the long tap.
452 pending_long_tap_.reset(
453 new ui::GestureEvent(*event,
454 static_cast<aura::Window*>(drag_drop_tracker_->capture_window()),
455 static_cast<aura::Window*>(drag_source_window_)));
456 DoDragCancel(kTouchCancelAnimationDuration);
457 break;
458 default:
459 break;
461 event->SetHandled();
464 void DragDropController::OnWindowDestroyed(aura::Window* window) {
465 if (drag_window_ == window)
466 drag_window_ = NULL;
467 if (drag_source_window_ == window)
468 drag_source_window_ = NULL;
471 ////////////////////////////////////////////////////////////////////////////////
472 // DragDropController, protected:
474 gfx::LinearAnimation* DragDropController::CreateCancelAnimation(
475 int duration,
476 int frame_rate,
477 gfx::AnimationDelegate* delegate) {
478 return new gfx::LinearAnimation(duration, frame_rate, delegate);
481 ////////////////////////////////////////////////////////////////////////////////
482 // DragDropController, private:
484 void DragDropController::AnimationEnded(const gfx::Animation* animation) {
485 cancel_animation_.reset();
487 // By the time we finish animation, another drag/drop session may have
488 // started. We do not want to destroy the drag image in that case.
489 if (!IsDragDropInProgress())
490 drag_image_.reset();
491 if (pending_long_tap_) {
492 // If not in a nested message loop, we can forward the long tap right now.
493 if (!should_block_during_drag_drop_)
494 ForwardPendingLongTap();
495 else {
496 // See comment about this in OnGestureEvent().
497 base::MessageLoopForUI::current()->PostTask(
498 FROM_HERE,
499 base::Bind(&DragDropController::ForwardPendingLongTap,
500 weak_factory_.GetWeakPtr()));
505 void DragDropController::DoDragCancel(int drag_cancel_animation_duration_ms) {
506 ash::Shell::GetInstance()->cursor_manager()->SetCursor(ui::kCursorPointer);
508 // |drag_window_| can be NULL if we have just started the drag and have not
509 // received any DragUpdates, or, if the |drag_window_| gets destroyed during
510 // a drag/drop.
511 aura::client::DragDropDelegate* delegate = drag_window_?
512 aura::client::GetDragDropDelegate(drag_window_) : NULL;
513 if (delegate)
514 delegate->OnDragExited();
516 Cleanup();
517 drag_operation_ = 0;
518 StartCanceledAnimation(drag_cancel_animation_duration_ms);
519 if (should_block_during_drag_drop_)
520 quit_closure_.Run();
523 void DragDropController::AnimationProgressed(const gfx::Animation* animation) {
524 gfx::Rect current_bounds = animation->CurrentValueBetween(
525 drag_image_initial_bounds_for_cancel_animation_,
526 drag_image_final_bounds_for_cancel_animation_);
527 drag_image_->SetBoundsInScreen(current_bounds);
530 void DragDropController::AnimationCanceled(const gfx::Animation* animation) {
531 AnimationEnded(animation);
534 void DragDropController::StartCanceledAnimation(int animation_duration_ms) {
535 DCHECK(drag_image_.get());
536 drag_image_->SetTouchDragOperationHintOff();
537 drag_image_initial_bounds_for_cancel_animation_ =
538 drag_image_->GetBoundsInScreen();
539 cancel_animation_.reset(CreateCancelAnimation(animation_duration_ms,
540 kCancelAnimationFrameRate,
541 this));
542 cancel_animation_->Start();
545 void DragDropController::ForwardPendingLongTap() {
546 if (drag_source_window_ && drag_source_window_->delegate()) {
547 drag_source_window_->delegate()->OnGestureEvent(pending_long_tap_.get());
548 DispatchGestureEndToWindow(drag_source_window_);
550 pending_long_tap_.reset();
551 if (drag_source_window_)
552 drag_source_window_->RemoveObserver(this);
553 drag_source_window_ = NULL;
556 void DragDropController::Cleanup() {
557 if (drag_window_)
558 drag_window_->RemoveObserver(this);
559 drag_window_ = NULL;
560 drag_data_ = NULL;
561 // Cleanup can be called again while deleting DragDropTracker, so use Pass
562 // instead of reset to avoid double free.
563 drag_drop_tracker_.Pass();
566 } // namespace ash