1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2006 Alexey Proskuryakov (ap (at) webkit.org) 4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 #include "core/page/AutoscrollController.h" 30 31 #include "core/page/EventHandler.h" 32 #include "core/page/Frame.h" 33 #include "core/page/FrameView.h" 34 #include "core/page/Page.h" 35 #include "core/rendering/HitTestResult.h" 36 #include "core/rendering/RenderBox.h" 37 #include "wtf/CurrentTime.h" 38 39 namespace WebCore { 40 41 // Delay time in second for start autoscroll if pointer is in border edge of scrollable element. 42 static double autoscrollDelay = 0.2; 43 44 // When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth 45 static const double autoscrollInterval = 0.05; 46 47 PassOwnPtr<AutoscrollController> AutoscrollController::create() 48 { 49 return adoptPtr(new AutoscrollController()); 50 } 51 52 AutoscrollController::AutoscrollController() 53 : m_autoscrollTimer(this, &AutoscrollController::autoscrollTimerFired) 54 , m_autoscrollRenderer(0) 55 , m_autoscrollType(NoAutoscroll) 56 , m_dragAndDropAutoscrollStartTime(0) 57 { 58 } 59 60 bool AutoscrollController::autoscrollInProgress() const 61 { 62 return m_autoscrollType == AutoscrollForSelection; 63 } 64 65 bool AutoscrollController::autoscrollInProgress(const RenderBox* renderer) const 66 { 67 return m_autoscrollRenderer == renderer; 68 } 69 70 void AutoscrollController::startAutoscrollForSelection(RenderObject* renderer) 71 { 72 // We don't want to trigger the autoscroll or the panScroll if it's already active 73 if (m_autoscrollTimer.isActive()) 74 return; 75 RenderBox* scrollable = RenderBox::findAutoscrollable(renderer); 76 if (!scrollable) 77 return; 78 m_autoscrollType = AutoscrollForSelection; 79 m_autoscrollRenderer = scrollable; 80 startAutoscrollTimer(); 81 } 82 83 void AutoscrollController::stopAutoscrollTimer() 84 { 85 RenderBox* scrollable = m_autoscrollRenderer; 86 m_autoscrollTimer.stop(); 87 m_autoscrollRenderer = 0; 88 89 if (!scrollable) 90 return; 91 92 scrollable->stopAutoscroll(); 93 #if OS(WINDOWS) 94 if (panScrollInProgress()) { 95 if (FrameView* view = scrollable->frame()->view()) { 96 view->removePanScrollIcon(); 97 view->setCursor(pointerCursor()); 98 } 99 } 100 #endif 101 102 m_autoscrollType = NoAutoscroll; 103 } 104 105 void AutoscrollController::stopAutoscrollIfNeeded(RenderObject* renderer) 106 { 107 if (m_autoscrollRenderer != renderer) 108 return; 109 m_autoscrollRenderer = 0; 110 m_autoscrollType = NoAutoscroll; 111 m_autoscrollTimer.stop(); 112 } 113 114 void AutoscrollController::updateAutoscrollRenderer() 115 { 116 if (!m_autoscrollRenderer) 117 return; 118 119 RenderObject* renderer = m_autoscrollRenderer; 120 121 #if OS(WINDOWS) 122 HitTestResult hitTest = renderer->frame()->eventHandler()->hitTestResultAtPoint(m_panScrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); 123 124 if (Node* nodeAtPoint = hitTest.innerNode()) 125 renderer = nodeAtPoint->renderer(); 126 #endif 127 128 while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) 129 renderer = renderer->parent(); 130 m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0; 131 } 132 133 void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime) 134 { 135 if (!dropTargetNode) { 136 stopAutoscrollTimer(); 137 return; 138 } 139 140 RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer()); 141 if (!scrollable) { 142 stopAutoscrollTimer(); 143 return; 144 } 145 146 Page* page = scrollable->frame() ? scrollable->frame()->page() : 0; 147 if (!page) { 148 stopAutoscrollTimer(); 149 return; 150 } 151 152 IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition); 153 if (offset.isZero()) { 154 stopAutoscrollTimer(); 155 return; 156 } 157 158 m_dragAndDropAutoscrollReferencePosition = eventPosition + offset; 159 160 if (m_autoscrollType == NoAutoscroll) { 161 m_autoscrollType = AutoscrollForDragAndDrop; 162 m_autoscrollRenderer = scrollable; 163 m_dragAndDropAutoscrollStartTime = eventTime; 164 startAutoscrollTimer(); 165 } else if (m_autoscrollRenderer != scrollable) { 166 m_dragAndDropAutoscrollStartTime = eventTime; 167 m_autoscrollRenderer = scrollable; 168 } 169 } 170 171 #if OS(WINDOWS) 172 void AutoscrollController::handleMouseReleaseForPanScrolling(Frame* frame, const PlatformMouseEvent& mouseEvent) 173 { 174 Page* page = frame->page(); 175 if (!page || page->mainFrame() != frame) 176 return; 177 switch (m_autoscrollType) { 178 case AutoscrollForPan: 179 if (mouseEvent.button() == MiddleButton) 180 m_autoscrollType = AutoscrollForPanCanStop; 181 break; 182 case AutoscrollForPanCanStop: 183 stopAutoscrollTimer(); 184 break; 185 } 186 } 187 188 bool AutoscrollController::panScrollInProgress() const 189 { 190 return m_autoscrollType == AutoscrollForPanCanStop || m_autoscrollType == AutoscrollForPan; 191 } 192 193 void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition) 194 { 195 // We don't want to trigger the autoscroll or the panScroll if it's already active 196 if (m_autoscrollTimer.isActive()) 197 return; 198 199 m_autoscrollType = AutoscrollForPan; 200 m_autoscrollRenderer = scrollable; 201 m_panScrollStartPos = lastKnownMousePosition; 202 203 if (FrameView* view = scrollable->frame()->view()) 204 view->addPanScrollIcon(lastKnownMousePosition); 205 startAutoscrollTimer(); 206 } 207 #else 208 bool AutoscrollController::panScrollInProgress() const 209 { 210 return false; 211 } 212 #endif 213 214 void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>*) 215 { 216 if (!m_autoscrollRenderer) { 217 stopAutoscrollTimer(); 218 return; 219 } 220 221 EventHandler* eventHandler = m_autoscrollRenderer->frame()->eventHandler(); 222 switch (m_autoscrollType) { 223 case AutoscrollForDragAndDrop: 224 if (WTF::currentTime() - m_dragAndDropAutoscrollStartTime > autoscrollDelay) 225 m_autoscrollRenderer->autoscroll(m_dragAndDropAutoscrollReferencePosition); 226 break; 227 case AutoscrollForSelection: 228 if (!eventHandler->mousePressed()) { 229 stopAutoscrollTimer(); 230 return; 231 } 232 eventHandler->updateSelectionForMouseDrag(); 233 m_autoscrollRenderer->autoscroll(eventHandler->lastKnownMousePosition()); 234 break; 235 case NoAutoscroll: 236 break; 237 #if OS(WINDOWS) 238 case AutoscrollForPanCanStop: 239 case AutoscrollForPan: 240 if (!panScrollInProgress()) { 241 stopAutoscrollTimer(); 242 return; 243 } 244 if (FrameView* view = m_autoscrollRenderer->frame()->view()) 245 updatePanScrollState(view, eventHandler->lastKnownMousePosition()); 246 m_autoscrollRenderer->panScroll(m_panScrollStartPos); 247 break; 248 #endif 249 } 250 } 251 252 void AutoscrollController::startAutoscrollTimer() 253 { 254 m_autoscrollTimer.startRepeating(autoscrollInterval); 255 } 256 257 #if OS(WINDOWS) 258 void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition) 259 { 260 // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll 261 // So we don't want to change the cursor over this area 262 bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius); 263 bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius); 264 bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius); 265 bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius); 266 267 if (m_autoscrollType == AutoscrollForPan && (east || west || north || south)) 268 m_autoscrollType = AutoscrollForPanCanStop; 269 270 if (north) { 271 if (east) 272 view->setCursor(northEastPanningCursor()); 273 else if (west) 274 view->setCursor(northWestPanningCursor()); 275 else 276 view->setCursor(northPanningCursor()); 277 } else if (south) { 278 if (east) 279 view->setCursor(southEastPanningCursor()); 280 else if (west) 281 view->setCursor(southWestPanningCursor()); 282 else 283 view->setCursor(southPanningCursor()); 284 } else if (east) 285 view->setCursor(eastPanningCursor()); 286 else if (west) 287 view->setCursor(westPanningCursor()); 288 else 289 view->setCursor(middlePanningCursor()); 290 } 291 #endif 292 293 } // namespace WebCore 294