webengine/osswebengine/WebKit/s60/webview/WebPageScrollHandler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 19 Mar 2010 09:52:28 +0200
changeset 65 5bfc169077b2
parent 42 d39add9822e2
permissions -rw-r--r--
Revision: 201006 Kit: 201011

/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   Implementation of drag scrolling
*
*/


// INCLUDE FILES
#include <Browser_platform_variant.hrh>
#include <../bidi.h>
#include "WebPageScrollHandler.h"
#include "BrCtl.h"
#include "WebFrame.h"
#include "WebFrameView.h"
#include "WebView.h"
#include <AknUtils.h>
#include "Page.h"
#include "Frame.h"
#include "WebCoreFrameBridge.h"
#include "FrameView.h"
#include "FocusController.h"
#include "PlatformScrollbar.h"
#include "WebScrollbarDrawer.h"
#include "RenderObject.h"
#include "WebScrollingDeceleratorGH.h"

#include "WebKitLogger.h"
using namespace WebCore;
using namespace RT_GestureHelper;
// constants
const int KPageOverviewScrollPeriodic = 20 * 1000; // Update frequently for faster, smoother scrolling
const int KMicroInterval = 300000;
const int KPageOverviewScrollStart = 1000;
const int KCancelDecelerationTimeout = 200000; //Decelerate only if flicked KCancelDecelerationTimeout microsec after last drag event.

const int KScrollIntervalTimeout = 40000; // scroll timer interval in microseconds
const int KAngularDeviationThreshold = 160; // % deviation ignored from minor axis of motion(120 means 20 %)
const int KScrollThresholdPixels = 10; // scrolls only if delta is above this threshold
const int KScrollDirectionBoundary = 30; // Bound around focal point within which scrolling locks in X or Y states
const float KTanOfThresholdAngle = 0.46; // tan of 25 degree

int handleScrollTimerEventCallback( TAny* ptr);

// ============================= LOCAL FUNCTIONS ===============================
int WebPageScrollHandler::pageOverviewScrollCallback( TAny* aPtr )
{
#ifdef BRDO_USE_GESTURE_HELPER
    static_cast<WebPageScrollHandler*>(aPtr)->scrollPageOverviewGH();
#else
    static_cast<WebPageScrollHandler*>(aPtr)->handlePageOverviewScrollCallback();
#endif //BRDO_USE_GESTURE_HELPER
    return KErrNone;
}
    
int handleScrollTimerEventCallback( TAny* ptr)
{
    WebPageScrollHandler* scrollHandler = static_cast<WebPageScrollHandler*>(ptr);
    scrollHandler->scrollContent();
    return EFalse;    
}      

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// ScrollableView::contentPos
// 
// -----------------------------------------------------------------------------
//
TPoint ScrollableView::contentPos()
{
    WebFrameView* fv = activeFrameView();
    if (fv) return fv->contentPos();
    return TPoint (0,0);
}

WebFrameView* ScrollableView::activeFrameView()
{
    if (m_scrollingElement) {
        return kit(m_scrollingElement->document()->frame())->frameView();
    }
    else {
        return m_frameView;
    }
}


// -----------------------------------------------------------------------------
// WebPageScrollHandler::NewL
// The two-phase Symbian constructor
// -----------------------------------------------------------------------------
//
WebPageScrollHandler* WebPageScrollHandler::NewL(WebView& webView)
    {
    WebPageScrollHandler* self = new (ELeave) WebPageScrollHandler( webView );
    CleanupStack::PushL(self);
    self->constructL();
    CleanupStack::Pop(); //self
    return self;
    }

// -----------------------------------------------------------------------------
// WebPageScrollHandler::WebPageScrollHandler
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
WebPageScrollHandler::WebPageScrollHandler(WebView& webView)
: m_webView( &webView ), m_decel(0), m_decelGH(NULL)
    {
    }

// -----------------------------------------------------------------------------
// WebPageScrollHandler::constructL
// The constructor that can contain code that might leave.
// -----------------------------------------------------------------------------
//
void WebPageScrollHandler::constructL()
    {
        m_scrollTimer = CPeriodic::NewL(CActive::EPriorityUserInput - 1);
        m_pageOverviewScrollPeriodic = CPeriodic::NewL(CActive::EPriorityUserInput - 1);
        m_lastPosition = TPoint(0, 0);
        m_scrollbarDrawer = WebScrollbarDrawer::NewL();
        if(AknLayoutUtils::PenEnabled()) {
            m_touchScrolling = true;
            m_scrollDirectionState = ScrollDirectionUnassigned;
            
#ifndef BRDO_USE_GESTURE_HELPER            
            m_decel = WebScrollingDecelerator::NewL(*m_webView);
#else            
	        m_decelGH = WebScrollingDeceleratorGH::NewL(*m_webView);
#endif	    
        }
        else  {
            m_touchScrolling = false;            
        }
    }

// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
WebPageScrollHandler::~WebPageScrollHandler()
{    
    
    if (m_scrollTimer) {
        m_scrollTimer->Cancel();
        delete m_scrollTimer;    
    }

    if (m_pageOverviewScrollPeriodic) {
        m_pageOverviewScrollPeriodic->Cancel();
        delete m_pageOverviewScrollPeriodic;
    }

    delete m_decel;
    delete m_decelGH;
    delete m_scrollbarDrawer;
}
    
void WebPageScrollHandler::handlePageOverviewScrollingL(const TPointerEvent& pointerEvent)
{
    switch (pointerEvent.iType){
        case TPointerEvent::EButton1Down:            
            m_webView->setViewIsScrolling(false);
            m_lastPointerEvent.iPosition.SetXY(0,0);
            m_lastDragEvent = pointerEvent;
            break;

        case TPointerEvent::EDrag:
            if (!m_webView->viewIsScrolling()){
                m_webView->setViewIsScrolling(true);
                m_pageOverviewScrollPeriodic->Start( 0, KPageOverviewScrollPeriodic, TCallBack(&pageOverviewScrollCallback, this));
            }
            break;

        case TPointerEvent::EButton1Up:
            if (m_pageOverviewScrollPeriodic->IsActive()){ 
                m_pageOverviewScrollPeriodic->Cancel();
            }
            m_webView->closePageView();
            m_webView->setViewIsScrolling(false);
            break;

        default:
            break;
   }
  
   // Scroll here only for EButton1Up and EButton1Down if needed
   if(pointerEvent.iType != TPointerEvent::EDrag && m_lastPointerEvent.iPosition != pointerEvent.iPosition){
       scrollPageOverview( pointerEvent );
   }
   m_lastPointerEvent = pointerEvent; 
}

void WebPageScrollHandler::handlePageOverviewScrollCallback()
{
    int absX = Abs( m_lastDragEvent.iPosition.iX - m_lastPointerEvent.iPosition.iX);
    int absY = Abs( m_lastDragEvent.iPosition.iY - m_lastPointerEvent.iPosition.iY);

   // Scrolling for EDrag events

    if( absX > 3 ||  absY > 3){
        scrollPageOverview( m_lastPointerEvent );
        m_lastDragEvent = m_lastPointerEvent;
    }
}


void WebPageScrollHandler::scrollPageOverview(const TPointerEvent& pointerEvent)
{

    TRect indicatorRect = m_webView->pageScaler()->IndicatorRect();
    TInt zoomLevel = m_webView->pageScaler()->ZoomOutLevel();
    TPoint currentPosition = m_webView->mainFrame()->frameView()->contentPos();
    TInt xInDoc = ((pointerEvent.iPosition.iX - indicatorRect.iTl.iX  - indicatorRect.Width() / 2) * zoomLevel ) / 100 + currentPosition.iX;
    TInt yInDoc = ((pointerEvent.iPosition.iY - indicatorRect.iTl.iY  - indicatorRect.Height() / 2) * zoomLevel ) / 100 + currentPosition.iY;
           
    m_webView->mainFrame()->frameView()->scrollTo(TPoint(xInDoc, yInDoc));    

}

void WebPageScrollHandler::updateScrolling(const TPointerEvent& pointerEvent)
{
    switch (pointerEvent.iType)
    {
        case TPointerEvent::EButton1Down:
        {        
            m_lastMoveEventTime = 0; 
            m_lastPosition = pointerEvent.iPosition;
            m_currentPosition = pointerEvent.iPosition;
            m_webView->setViewIsScrolling(false);         
            // stop deceleration scrolling cycle            
            if (m_decel) {
                m_decel->stopDecelL();    
            }
            m_scrollableView.m_scrollingElement = NULL;
            m_scrollableView.m_frameView = NULL;
            break;
        }
        case TPointerEvent::EButton1Up:
        {
            m_scrollTimer->Cancel();
            if (m_scrollableView.m_scrollingElement) {
                clearScrollingElement();
            }
            else {
                if (m_lastMoveEventTime != 0)
                {           
                    // Start deceleration only if the delta since last drag event is less than threshold
                    TTime now;
                    now.HomeTime();             
                    TTimeIntervalMicroSeconds ms = now.MicroSecondsFrom(m_lastMoveEventTime);
                    if (ms < KCancelDecelerationTimeout) {
                        m_decel->startDecelL();    
                    }
                }            
                if (m_webView->viewIsScrolling()) {
                    Frame* frame = m_webView->page()->focusController()->focusedOrMainFrame();
                    frame->bridge()->sendScrollEvent();                            
                }
            }
            m_scrollbarDrawer->fadeScrollbar();
            m_scrollDirectionState = ScrollDirectionUnassigned;
            m_lastMoveEventTime = 0;
            break;
        }
    }
}

void WebPageScrollHandler::setupScrolling(const TPoint& aNewPosition)
{
    if (m_lastPosition == TPoint(0, 0)) {
        m_lastPosition = aNewPosition;
    }
   
    if(m_lastPosition == aNewPosition)
        return; // no displacement -- means no need for scrolling    

    //Ignore move events until they jump the threshold (avoids jittery finger effect) 
    TInt absX = Abs( aNewPosition.iX - m_lastPosition.iX);
    TInt absY = Abs( aNewPosition.iY - m_lastPosition.iY);
    if( absX < KScrollThresholdPixels &&  absY < KScrollThresholdPixels)
        return;
            
    if (calculateScrollableFrameView(aNewPosition))  {

        // normalize current position to minimize cumulative rounding error 
        m_currentNormalizedPosition.iX = m_scrollableView.contentPos().iX * 100;
        m_currentNormalizedPosition.iY = m_scrollableView.contentPos().iY * 100;
        // do the scrolling in a time out
        m_scrollTimer->Cancel();                   
        m_scrollTimer->Start( 0, KScrollIntervalTimeout, TCallBack(&handleScrollTimerEventCallback,this));
        m_webView->setViewIsScrolling(true);
        m_webView->toggleRepaintTimer(false);
    }
}


void WebPageScrollHandler::clearScrollingElement()
{
    if (m_scrollableView.m_scrollingElement) {
        m_scrollableView.m_scrollingElement->deref();
        m_scrollableView.m_scrollingElement = NULL;
    }
}

void WebPageScrollHandler::scrollContent()
{
    TPoint scrollDelta = m_lastPosition - m_currentPosition;
    scrollContent(scrollDelta);
}

void WebPageScrollHandler::scrollContent(TPoint& aScrollDelta)
{
    if(!m_scrollableView.activeFrameView())
            return;
        
    
    int absX = Abs(aScrollDelta.iX);
    int absY = Abs(aScrollDelta.iY);
                                       
    if(absX || absY) //move only if necessary
    {      
        // calculate which direction we are trying to scroll
        if(m_scrollDirectionState == ScrollDirectionUnassigned) {
            m_focalPoint = m_currentPosition;
            calculateScrollDirection(absX, absY);
        }   
        
        switch (m_scrollDirectionState)
        {           
            case ScrollDirectionX: //scroll in X dir
            {
                aScrollDelta.iY = 0;
                aScrollDelta.iX *= 100;
                //Fallback to XY state if the current position is out of bounds                
                TPoint boundaryCheckpoint = m_focalPoint - m_currentPosition;                                
                if(Abs(boundaryCheckpoint.iY) > KScrollDirectionBoundary)
                    m_scrollDirectionState = ScrollDirectionXY;
                break;
            }
            case ScrollDirectionY: //scroll in Y dir
            {                
                aScrollDelta.iX = 0;
                aScrollDelta.iY *= 100;
                //Fallback to XY state if the current position is out of bounds                
                TPoint boundaryCheckpoint = m_focalPoint - m_currentPosition;                                
                if(Abs(boundaryCheckpoint.iX) > KScrollDirectionBoundary)
                    m_scrollDirectionState = ScrollDirectionXY;
                break;
            }
            case ScrollDirectionXY: //scroll in XY
            {
                aScrollDelta.iX *= 100;
                aScrollDelta.iY *= 100;
                m_scrollDirectionState = ScrollDirectionUnassigned;
                break;
            }
        }        
        if (m_scrollableView.m_scrollingElement) {
            bool shouldScrollVertically = false;
            bool shouldScrollHorizontally = false;
            WebFrame* frame = kit(m_scrollableView.m_scrollingElement->document()->frame());
            RenderObject* render = m_scrollableView.m_scrollingElement->renderer();
            __ASSERT_DEBUG(render->isScrollable(), User::Panic(_L(""), KErrGeneral));
            if (aScrollDelta.iY)
                shouldScrollVertically = !render->scroll(ScrollDown, ScrollByPixel, frame->frameView()->toDocCoords(aScrollDelta).iY / 100);
            if (aScrollDelta.iX)
                shouldScrollHorizontally = !render->scroll(ScrollRight, ScrollByPixel, frame->frameView()->toDocCoords(aScrollDelta).iX / 100);

            TPoint scrollPos = frame->frameView()->contentPos();
            TPoint newscrollDelta = frame->frameView()->toDocCoords(aScrollDelta);
            m_currentNormalizedPosition +=  newscrollDelta;     

            if (shouldScrollHorizontally) {
                scrollPos.iX = m_currentNormalizedPosition.iX/100;
            }
            if (shouldScrollVertically) {
                scrollPos.iY = m_currentNormalizedPosition.iY/100;
            }
            
            
            if (shouldScrollVertically || shouldScrollHorizontally){
                if (m_scrollableView.m_frameView->needScroll(scrollPos)) {
                    frame->frameView()->scrollTo(scrollPos);
                    updateScrollbars(scrollPos, newscrollDelta);
                    core(frame)->sendScrollEvent();
                }
            }
            else {
                m_webView->syncRepaint();
            }
            m_lastPosition = m_currentPosition;
            m_currentNormalizedPosition.iX = frame->frameView()->contentPos().iX * 100;
            m_currentNormalizedPosition.iY = frame->frameView()->contentPos().iY * 100;
        }
        else {
            TPoint scrollPos;
            TPoint newscrollDelta = m_scrollableView.m_frameView->toDocCoords(aScrollDelta);
            m_currentNormalizedPosition +=  newscrollDelta;  
            scrollPos.iX = m_currentNormalizedPosition.iX/100;
            scrollPos.iY = m_currentNormalizedPosition.iY/100;
            TPoint cpos = m_scrollableView.m_frameView->contentPos();
           
            if(!m_scrollableView.m_frameView->needScroll(scrollPos)) {
                m_scrollDirectionState = ScrollDirectionUnassigned;
                m_lastPosition = m_currentPosition;
                m_currentNormalizedPosition.iX = m_scrollableView.contentPos().iX * 100;
                m_currentNormalizedPosition.iY = m_scrollableView.contentPos().iY * 100;
            }
            else {
          
                m_scrollableView.m_frameView->scrollTo(scrollPos);
                m_lastPosition = m_currentPosition;
#ifndef BRDO_USE_GESTURE_HELPER                
                m_decel->updatePos();
#endif                
            // update scroll bars
                updateScrollbars(scrollPos, newscrollDelta);
            }
        }
    }
} 

bool WebPageScrollHandler::calculateScrollableFrameView(const TPoint& aNewPosition)
{
    calculateScrollableElement(aNewPosition);
    
    //First figure out the direction we are scrolling    
    bool x_r = false;
    bool x_l = false;
    bool y_t = false;
    bool y_b = false;

    //find the x direction
    int x_delta = aNewPosition.iX - m_lastPosition.iX;
    if (x_delta > 0) {
        x_l = true;                
    } else if (x_delta < 0){
        x_r = true;
    }

    //find the y direction
    int y_delta = aNewPosition.iY - m_lastPosition.iY;
    if (y_delta > 0) {
        y_t = true;                
    } else if (y_delta < 0){
        y_b = true;
    }
        
    m_scrollableView.m_frameView = NULL;
        
    //find the frame that can scroll if we can't scroll check the parents        
    WebFrame* frame = m_webView->mainFrame()->frameAtPoint(aNewPosition);
    for (; frame; frame = frame->parentFrame())  {
    
        // Dont scroll a frameset
        if (frame->parentFrame() && frame->parentFrame()->isFrameSet()) 
            continue;     
        
        TPoint contentpos = frame->frameView()->contentPos();
        TSize contentsize = frame->frameView()->contentSize();        
        TRect framerect = frame->frameView()->rect();        
        
        //if the content width is > frameview width
        if (contentsize.iWidth > framerect.Width() && frame->frameView()->hScrollbar()->isEnabled()) {
            //IF we are trying to scroll to the right we need 
            //to see if the content position < content size - frameview size        
            //ELSE if we are trying to the left we need 
            //to see if the content position > 0 
            if (x_r) {        
                if (contentpos.iX < (contentsize.iWidth - framerect.Width())) {
                    m_scrollableView.m_frameView = frame->frameView();
                    return true;                
                }

            } else if (x_l) {
                if (contentpos.iX > 0) {
                    m_scrollableView.m_frameView = frame->frameView();
                    return true;                
                }            
            }
        }
        
        //if the content height is > frameview height
        if (contentsize.iHeight > framerect.Height() && frame->frameView()->vScrollbar()->isEnabled()) {
            //IF we are trying to scroll down we need 
            //to see if the content position < content size - frameview size        
            //ELSE if we are trying to scroll up we need 
            //to see if the content position > 0 
            if (y_b) {        
                if (contentpos.iY < (contentsize.iHeight - framerect.Height())) {
                    m_scrollableView.m_frameView = frame->frameView();
                    return true;                
                }

            } else if (y_t) {
                if (contentpos.iY > 0) {
                    m_scrollableView.m_frameView = frame->frameView();
                    return true;                
                }            
            }
        }
    }
    
    //could not find a scrollable frame        
    m_scrollableView.m_frameView  = m_webView->mainFrame()->frameView();
    return true;
   
}

void WebPageScrollHandler::calculateScrollDirection(int absX, int absY)
{
     //assign scroll direction state according to scroll angle
    if((absX * KAngularDeviationThreshold) < (absY * 100))
        m_scrollDirectionState = ScrollDirectionY;
    else if((absY * KAngularDeviationThreshold) < (absX * 100))
        m_scrollDirectionState = ScrollDirectionX;
    else
        m_scrollDirectionState = ScrollDirectionXY;    
}

void WebPageScrollHandler::updateScrollbars(const TPoint& scrollPos, TPoint& newscrollDelta)
{
    WebFrameView* fv = m_scrollableView.activeFrameView();
    if (fv) {
        
        if (fv->vScrollbar()) {
            fv->vScrollbar()->setValue(scrollPos.iY);
        }

        if (fv->hScrollbar()) {
            fv->hScrollbar()->setValue(scrollPos.iX);
        }
        
        if ((fv->frame() == m_webView->mainFrame())) {
          m_scrollbarDrawer->drawScrollbar(m_webView, newscrollDelta);
        }
    }
}
         

bool WebPageScrollHandler::calculateScrollableElement(const TPoint& aNewPosition)
{
    WebFrame* frame = m_webView->mainFrame()->frameAtPoint(aNewPosition);
    if( !frame ) return false;
    TPoint pt = frame->frameView()->viewCoordsInFrameCoords(aNewPosition);
    Element* e = core(frame)->document()->elementFromPoint(pt.iX, pt.iY);
    Element* currElement = NULL;
    if(!e) return NULL;
    RenderObject* render = e->renderer();
    if (render && render->isScrollable()) {
        RenderLayer* layer = render->enclosingLayer();
        Element* parent = e;
        currElement = e;
        while (!currElement->isControl() && parent && parent->renderer() && parent->renderer()->enclosingLayer() == layer) {
            currElement = parent;
            Node* pn = parent;
            do {
                pn = pn->parent();
            } while (pn && !pn->isElementNode());
            parent = static_cast<Element*>(pn);
        }
        if (currElement) {
            currElement->ref();
            m_scrollableView.m_scrollingElement = currElement; 
            m_scrollableView.m_frameView = NULL;
            return true;
        }
    }
    return false;
}


void WebPageScrollHandler::scrollPageOverviewGH()
{

    TRect indicatorRect = m_webView->pageScaler()->IndicatorRect();
    TInt zoomLevel = m_webView->pageScaler()->ZoomOutLevel();
    TPoint currentPosition = m_webView->mainFrame()->frameView()->contentPos();
    TInt xInDoc = ((m_currentPosition.iX - indicatorRect.iTl.iX  - indicatorRect.Width() / 2) * zoomLevel ) / 100 + currentPosition.iX;
    TInt yInDoc = ((m_currentPosition.iY - indicatorRect.iTl.iY  - indicatorRect.Height() / 2) * zoomLevel ) / 100 + currentPosition.iY;
           
    m_webView->mainFrame()->frameView()->scrollTo(TPoint(xInDoc, yInDoc));    

}


void WebPageScrollHandler::handleScrollingGH(const TGestureEvent& aEvent)
{   
    TPoint newPos = aEvent.CurrentPos();
    m_currentPosition = newPos;
    if (m_webView->inPageViewMode()) {
        if (!m_pageOverviewScrollPeriodic->IsActive()){
            m_pageOverviewScrollPeriodic->Start( 0, KPageOverviewScrollPeriodic, 
                                                TCallBack(&pageOverviewScrollCallback, this));
            m_webView->setViewIsScrolling(true);
            m_webView->toggleRepaintTimer(false);
        }
    }
    else if (!m_webView->viewIsScrolling()) {
        setupScrolling(newPos);    
    }
}


void WebPageScrollHandler::handleTouchDownGH(const TGestureEvent& aEvent)
{
    TPoint newPos = aEvent.CurrentPos();
    m_lastMoveEventTime = 0; 
    m_lastPosition = newPos;
    m_currentPosition = newPos;
    m_webView->setViewIsScrolling(false);      
    m_webView->toggleRepaintTimer(true);
            
    if (m_decelGH) {
        m_decelGH->cancelDecel();
    }
    m_scrollableView.m_scrollingElement = NULL;
    m_scrollableView.m_frameView = NULL;
}


void WebPageScrollHandler::handleTouchUpGH(const TGestureEvent& aEvent)
{
    bool decelDoesScrollbars = false;
    TPoint newPos = aEvent.CurrentPos();

    if (m_webView->inPageViewMode()) {
        if (m_pageOverviewScrollPeriodic->IsActive()){ 
            m_pageOverviewScrollPeriodic->Cancel();
        }
        m_webView->closePageView();
        scrollPageOverviewGH();
        m_webView->setViewIsScrolling(false);
        m_webView->toggleRepaintTimer(true);
    }
    else {
        m_scrollTimer->Cancel();
        m_lastPosition = TPoint(0, 0);
        decelDoesScrollbars = startDeceleration(aEvent);
                    
        if (m_webView->viewIsScrolling()) {
            Frame* frame = m_webView->page()->focusController()->focusedOrMainFrame();
            frame->bridge()->sendScrollEvent();                            
        }
    
        if (!decelDoesScrollbars) {
            m_scrollbarDrawer->fadeScrollbar();
            m_webView->setViewIsScrolling(false);
            m_webView->toggleRepaintTimer(true);
        }
        m_scrollDirectionState = ScrollDirectionUnassigned;
        m_lastMoveEventTime = 0;
    }
}


bool WebPageScrollHandler::startDeceleration(const TGestureEvent& aEvent)
{
    bool started = false;
    TRealPoint gstSpeed = aEvent.Speed();
    if (Abs(gstSpeed.iX / gstSpeed.iY) <= KTanOfThresholdAngle) {
       gstSpeed.iX = 0;
    }
    
    if (Abs(gstSpeed.iY / gstSpeed.iX) <= KTanOfThresholdAngle) { 
       gstSpeed.iY = 0;
    }
    
    if ((Abs(gstSpeed.iX) > 0) || (Abs(gstSpeed.iY) > 0)) {
       m_decelGH->startDecel(gstSpeed, m_scrollbarDrawer); 
       started = true;
    }
    
    
    return started;
}

//  End of File