- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / panels / mouse_drag_controller.mm
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 "chrome/browser/ui/cocoa/panels/mouse_drag_controller.h"
6
7 #include <Carbon/Carbon.h>  // kVK_Escape
8 #import <Cocoa/Cocoa.h>
9
10 #include "base/logging.h"
11 #include "base/mac/scoped_nsautorelease_pool.h"
12
13 // The distance the user has to move the mouse while keeping the left button
14 // down before panel resizing operation actually starts.
15 const double kDragThreshold = 3.0;
16
17 @implementation MouseDragController
18
19 - (NSView<MouseDragControllerClient>*)client {
20   return client_;
21 }
22
23 - (NSPoint)initialMouseLocation {
24   return initialMouseLocation_;
25 }
26
27 - (BOOL)exceedsDragThreshold:(NSPoint)mouseLocation {
28   float deltaX = fabs(initialMouseLocation_.x - mouseLocation.x);
29   float deltaY = fabs(initialMouseLocation_.y - mouseLocation.y);
30   return deltaX > kDragThreshold || deltaY > kDragThreshold;
31 }
32
33 - (BOOL)tryStartDrag:(NSEvent*)event {
34   DCHECK(dragState_ == PANEL_DRAG_CAN_START);
35   NSPoint mouseLocation = [event locationInWindow];
36   if (![self exceedsDragThreshold:mouseLocation])
37     return NO;
38
39   // Mouse moved over threshold, start drag.
40   dragState_ = PANEL_DRAG_IN_PROGRESS;
41   [client_ dragStarted:initialMouseLocation_];
42   return YES;
43 }
44
45 - (void)cleanupAfterDrag {
46   if (dragState_ == PANEL_DRAG_SUPPRESSED)
47     return;
48   dragState_ = PANEL_DRAG_SUPPRESSED;
49   initialMouseLocation_ = NSZeroPoint;
50   [client_ cleanupAfterDrag];
51 }
52
53 - (MouseDragController*)initWithClient:
54       (NSView<MouseDragControllerClient>*)client {
55   client_ = client;
56   dragState_ = PANEL_DRAG_SUPPRESSED;
57   return self;
58 }
59
60 - (void)mouseDown:(NSEvent*)event {
61   DCHECK(dragState_ == PANEL_DRAG_SUPPRESSED);
62   dragState_ = PANEL_DRAG_CAN_START;
63   initialMouseLocation_ = [event locationInWindow];
64   [client_ prepareForDrag];
65 }
66
67 - (void)mouseDragged:(NSEvent*)event {
68   if (dragState_ == PANEL_DRAG_SUPPRESSED)
69     return;
70
71   // In addition to events needed to control the drag operation, fetch the right
72   // mouse click events and key down events and ignore them, to prevent their
73   // accumulation in the queue and "playing out" when the mouse is released.
74   const NSUInteger mask =
75       NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSKeyUpMask |
76       NSRightMouseDownMask | NSKeyDownMask ;
77
78   while (true) {
79     base::mac::ScopedNSAutoreleasePool autorelease_pool;
80     BOOL keepGoing = YES;
81
82     switch ([event type]) {
83       case NSLeftMouseDragged: {
84         // If drag didn't start yet, see if mouse moved far enough to start it.
85         if (dragState_ == PANEL_DRAG_CAN_START && ![self tryStartDrag:event])
86           return;
87
88         DCHECK(dragState_ == PANEL_DRAG_IN_PROGRESS);
89         [client_ dragProgress:[event locationInWindow]];
90         break;
91       }
92
93       case NSKeyUp:
94         if ([event keyCode] == kVK_Escape) {
95           // The drag might not be started yet because of threshold, so check.
96           if (dragState_ == PANEL_DRAG_IN_PROGRESS)
97             [client_ dragEnded:YES];
98           keepGoing = NO;
99         }
100         break;
101
102       case NSLeftMouseUp:
103         // The drag might not be started yet because of threshold, so check.
104         if (dragState_ == PANEL_DRAG_IN_PROGRESS)
105           [client_ dragEnded:NO];
106         keepGoing = NO;
107         break;
108
109       case NSRightMouseDownMask:
110         break;
111
112       default:
113         // Dequeue and ignore other mouse and key events so the Chrome context
114         // menu does not come after right click on a page during Panel
115         // resize, or the keystrokes are not 'accumulated' and entered
116         // at once when the drag ends.
117         break;
118     }
119
120     if (!keepGoing)
121       break;
122
123     autorelease_pool.Recycle();
124
125     event = [NSApp nextEventMatchingMask:mask
126                                untilDate:[NSDate distantFuture]
127                                   inMode:NSDefaultRunLoopMode
128                                  dequeue:YES];
129
130   }
131   [self cleanupAfterDrag];
132 }
133
134
135 - (void)mouseUp:(NSEvent*)event {
136   if (dragState_ == PANEL_DRAG_SUPPRESSED)
137     return;
138   // The mouseUp while in drag should be processed by nested message loop
139   // in mouseDragged: method.
140   DCHECK(dragState_ != PANEL_DRAG_IN_PROGRESS);
141   // Do cleanup in case the actual drag was not started (because of threshold).
142   [self cleanupAfterDrag];
143 }
144 @end
145