--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/webkitutils/rt_gesturehelper/src/gesturehelperimpl.cpp Fri Mar 19 09:52:28 2010 +0200
@@ -0,0 +1,631 @@
+/*
+* 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: Gesture helper implementation
+*
+*/
+
+
+#include "gesturehelperimpl.h"
+
+#include <e32base.h>
+#include <w32std.h>
+
+#include "gesture.h"
+#include "gesturedefs.h"
+#include "utils.h"
+#include "gestureeventfilter.h"
+#include "gesturehelpereventsender.h"
+#include "flogger.h"
+
+using namespace RT_GestureHelper;
+
+namespace RT_GestureHelper
+{
+
+/// type of function in gesture helper to be called by the timer
+/// when timer triggers
+typedef void (CGestureHelperImpl::*CallbackFunctionL)();
+
+NONSHARABLE_CLASS( CCallbackTimer ) : public CTimer
+ {
+public:
+ /** Two-phase constructor */
+ static CCallbackTimer* NewL( CGestureHelperImpl& aHelper,
+ CallbackFunctionL aCallbackFunctionL, TInt aDelay, TBool aIsEnabled )
+ {
+ CCallbackTimer* self = new ( ELeave ) CCallbackTimer( aHelper,
+ aCallbackFunctionL, aDelay, aIsEnabled );
+ CleanupStack::PushL( self );
+ self->ConstructL(); // construct base class
+ CActiveScheduler::Add( self );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+ /** Destructor */
+ ~CCallbackTimer()
+ {
+ Cancel();
+ }
+
+ /** Set whether sending holding events is currently enabled */
+ void SetEnabled( TBool aEnabled )
+ {
+ iIsEnabled = aEnabled;
+ // cancel in case hold timer is already running
+ Cancel();
+ }
+
+ /** @return whether sending holding events is currently enabled */
+ TBool IsEnabled() const
+ {
+ return iIsEnabled;
+ }
+
+ /** Start the timer. Calls CGestureHelperImpl::StartHoldingL upon completion */
+ void Start()
+ {
+ // if sending hold events is disabled, do not ever start the hold timer, and
+ // hence hold events will never be triggered
+ if ( iIsEnabled )
+ {
+ Cancel();
+ After( iDelay );
+ }
+ }
+ void SetDelay(TInt aDelay) { iDelay = aDelay; }
+ TInt GetDelay() { return iDelay; }
+
+private:
+ /** Constructor */
+ CCallbackTimer( CGestureHelperImpl& aHelper,
+ CallbackFunctionL aCallbackFunctionL, TInt aDelay, TBool aIsEnabled )
+ : CTimer( EPriorityUserInput - 1 ), // give higher priority to new pointer events with - 1
+ iHelper( aHelper ), iCallbackFunctionL( aCallbackFunctionL ),
+ iDelay( aDelay ), iIsEnabled( aIsEnabled )
+ {
+ }
+
+ void RunL() // From CActive
+ {
+ (iHelper.*iCallbackFunctionL)();
+ }
+
+private:
+ /// helper object that will be called back when timer is triggered
+ CGestureHelperImpl& iHelper;
+ /// Function in the iHelper object call
+ CallbackFunctionL iCallbackFunctionL;
+ /// How long a time to wait befor calling back after Start()
+ TInt iDelay;
+ /// whether sending holding events is currently enabled
+ TBool iIsEnabled;
+ };
+
+} // namespace GestureHelper
+
+/**
+ * @return position from event. Use this instead of using aEvent direction to
+ * avoid accidentally using TPointerEvent::iPosition
+ */
+inline TPoint Position( const TPointerEvent& aEvent )
+ {
+ // use parent position, since the capturer is using full screen area,
+ // and because the (Alfred) drag events are not local to visual even when
+ // coming from the client
+
+ return aEvent.iPosition;
+ }
+
+// ----------------------------------------------------------------------------
+// Two-phase constructor
+// ----------------------------------------------------------------------------
+//
+CGestureHelperImpl* CGestureHelperImpl::NewL( MGestureObserver& aObserver )
+ {
+ CGestureHelperImpl* self = new ( ELeave ) CGestureHelperImpl( aObserver );
+ CleanupStack::PushL( self );
+ self->iEventSender = CGestureEventSender::NewL( aObserver );
+ self->iDoubleTapTimer = CCallbackTimer::NewL( *self, EmitFirstTapEvent,
+ KMaxTapDuration, EFalse ); // double tap is disabled by default
+ self->iHoldingTimer = CCallbackTimer::NewL( *self, StartHoldingL,
+ KHoldDuration, EFalse ); // holding is enabled by default
+
+ self->iLongTouchTimer = CCallbackTimer::NewL( *self, HandleLongTouch,
+ KLongTapDuration, ETrue ); // holding is enabled by default
+
+ self->iGesture = new ( ELeave ) CGesture();
+ self->iUnusedGesture = new ( ELeave ) CGesture();
+ TInt tapLimit = Mm2Pixels(KFingerSize_mm) / 2;
+ self->iEventFilter = new (ELeave) CGestureEventFilter(tapLimit);
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+// ----------------------------------------------------------------------------
+// Constructor
+// ----------------------------------------------------------------------------
+//
+CGestureHelperImpl::CGestureHelperImpl( MGestureObserver& aObserver )
+ : iObserver( aObserver )
+ {
+ }
+
+// ----------------------------------------------------------------------------
+// Destructor
+// ----------------------------------------------------------------------------
+//
+CGestureHelperImpl::~CGestureHelperImpl()
+ {
+ delete iDoubleTapTimer;
+ delete iHoldingTimer;
+ delete iGesture;
+ delete iPreviousTapGesture;
+ delete iUnusedGesture;
+ delete iLongTouchTimer;
+ delete iEventFilter;
+ delete iEventSender;
+ }
+
+
+// ----------------------------------------------------------------------------
+// SetHoldingEnabled
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::SetHoldingEnabled( TBool aEnabled )
+ {
+ iHoldingTimer->SetEnabled( aEnabled );
+ }
+
+// ----------------------------------------------------------------------------
+// IsHoldingEnabled
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::IsHoldingEnabled() const
+ {
+ return iHoldingTimer->IsEnabled();
+ }
+
+// ----------------------------------------------------------------------------
+// SetHoldingEnabled
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::SetDoubleTapEnabled( TBool aEnabled )
+ {
+ iDoubleTapTimer->SetEnabled( aEnabled );
+ }
+
+// ----------------------------------------------------------------------------
+// IsHoldingEnabled
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::IsDoubleTapEnabled() const
+ {
+ return iDoubleTapTimer->IsEnabled();
+ }
+
+
+
+// ----------------------------------------------------------------------------
+// Reset state
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::Reset()
+ {
+ iHoldingTimer->Cancel();
+ iLongTouchTimer->Cancel();
+ iGesture->Reset();
+ }
+
+/**
+ * Helper function that calls Reset on the pointer to CGestureHelperImpl
+ */
+static void ResetHelper( TAny* aHelper )
+ {
+ static_cast< CGestureHelperImpl* >( aHelper )->Reset();
+ }
+
+// ----------------------------------------------------------------------------
+// Handle a pointer event
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::HandlePointerEventL( const TPointerEvent& aEvent )
+ {
+ TInt filterReason;
+ SetLastEventTime();
+ if (!iEventFilter->FilterDrag(aEvent, iLastEventTime, filterReason))
+ {
+ return noneAlf_HandlePointerEventL( aEvent );
+ }
+ else
+ {
+ /*
+ TBuf<10> num;
+ num.Num( filterReason );
+ TBuf<128> str;
+ str.AppendFormat(_L("Filter reason: %d"), filterReason);
+ RFileLogger::Write( _L("gh"), _L("gh.txt"), EFileLoggingModeAppend, str);
+ */
+ return EFalse;
+ }
+ }
+
+
+TBool CGestureHelperImpl::noneAlf_HandlePointerEventL( const TPointerEvent& aEvent)
+ {
+ switch ( aEvent.iType )
+ {
+ case TPointerEvent::EButton1Down:
+ {
+ HandleTouchDownL(aEvent);
+ break;
+ }
+ case TPointerEvent::EDrag:
+ {
+ HandleMoveL(aEvent);
+ break;
+ }
+ case TPointerEvent::EButton1Up:
+ {
+ if (KErrNone == AddPoint( aEvent ))
+ {
+ HandleTouchUp(aEvent);
+ }
+ else
+ {
+ EmitCancelEvent();
+ }
+ Reset();
+ break;
+ }
+ default:
+ break;
+ }
+ return ETrue;
+ }
+
+TBool CGestureHelperImpl::IsMovementGesture(TGestureCode aCode)
+ {
+ return (aCode == EGestureDrag || aCode == EGestureFlick || aCode == EGestureSwipeUp ||
+ aCode == EGestureSwipeDown || aCode == EGestureSwipeRight || aCode == EGestureSwipeLeft);
+ }
+
+void CGestureHelperImpl::HandleLongTouch()
+ {
+ iDoubleTapTimer->Cancel();
+ iGesture->SetLongTap(ETrue);
+ iGesture->SetComplete();
+ TPoint startPos = iGesture->StartPos();
+ EmitEvent(*iGesture);
+ iGesture->Reset();
+ iGesture->AddPoint( startPos, GetLastEventTime() );
+ }
+
+void CGestureHelperImpl::HandleTouchDownL(const TPointerEvent& aEvent)
+ {
+ TGestureCode prevCode = iGesture->PreviousGestureCode();
+ if (prevCode == EGestureStart) return;
+ if (prevCode == EGestureDrag)
+ {
+ iGesture->Reset();
+ }
+ AddPointL( aEvent );
+
+ if (!iLongTouchTimer->IsActive())
+ {
+ iLongTouchTimer->Start();
+ }
+ if (!iDoubleTapTimer->IsActive())
+ {
+ EmitEvent( *iGesture );
+ }
+ }
+
+void CGestureHelperImpl::HandleMoveL(const TPointerEvent& aEvent)
+ {
+ if (iGesture->IsLatestPoint( Position(aEvent))) return; // I'm not sure we need this
+ //Cancel double tap time - it's neither tap nor double tap
+ iDoubleTapTimer->Cancel();
+ iLongTouchTimer->Cancel();
+
+ TBool isFirstPoint = IsIdle();
+
+ AddPointL( aEvent );
+
+ if (iPreviousTapGesture)
+ {
+ RecycleGesture(iPreviousTapGesture);
+ }
+
+ if (!isFirstPoint)
+ {
+ EmitEvent( *iGesture );
+ }
+ }
+
+void CGestureHelperImpl::HandleTouchUp(const TPointerEvent& /*aEvent*/)
+ {
+ TGestureCode prevCode = iGesture->PreviousGestureCode();
+ iLongTouchTimer->Cancel();
+ iDoubleTapTimer->Cancel();
+ TInt64 fromLastTouchUp = iLastEventTime.MicroSecondsFrom(iLastTouchUpTime).Int64();
+ TInt64 fromLastDoubleTap = iLastEventTime.MicroSecondsFrom(iLastDoubleTapTime).Int64();
+ /*
+ TBuf<1024> str;
+ str.AppendFormat(_L("fromLastTouchUp: %d, "), fromLastTouchUp);
+ str.AppendFormat(_L("fromLastDoubleTap: %d, "), fromLastTouchUp);
+ str.AppendFormat(_L("iPreviousTapGesture: %d, "), iPreviousTapGesture);
+ RFileLogger::Write( _L("gh"), _L("gh.txt"), EFileLoggingModeAppend, str);
+ */
+ if ( prevCode == EGestureLongTap )
+ {
+ EmitReleasedEvent();
+ }
+ else if (IsMovementGesture(prevCode) ||
+ !iDoubleTapTimer->IsEnabled() /* || !iGesture->IsTap()*/ )
+ {
+ iGesture->SetComplete();
+ EmitEvent(*iGesture);
+ }
+
+ else
+ {
+ if ( iPreviousTapGesture &&
+ (fromLastTouchUp > KDoubleTapMinActivationInterval) &&
+ (fromLastTouchUp < KDoubleTapMaxActivationInterval) &&
+ (fromLastDoubleTap > KDoubleTapIdleInterval))
+ {
+ // it's a double tap
+ iLastTouchUpTime = iLastEventTime;
+ iLastDoubleTapTime = iLastEventTime;
+ EmitDoubleTapEvent();
+ }
+ else
+ {
+ // it's a first tap
+ iLastTouchUpTime = iLastEventTime;
+ if (iPreviousTapGesture)
+ {
+ RecycleGesture(iPreviousTapGesture);
+ }
+
+ iPreviousTapGesture = iGesture;
+ iGesture = NewGesture();
+ iDoubleTapTimer->Start();
+ }
+ }
+ }
+
+
+
+void CGestureHelperImpl::EmitDoubleTapEvent()
+ {
+ iPreviousTapGesture->SetDoubleTap();
+ EmitFirstTapEvent();
+ }
+
+
+void CGestureHelperImpl::EmitReleasedEvent()
+ {
+ iGesture->SetComplete();
+ iGesture->SetReleased();
+ EmitEvent(*iGesture);
+ }
+
+
+// ----------------------------------------------------------------------------
+// Is the helper idle?
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline TBool CGestureHelperImpl::IsIdle() const
+ {
+ return iGesture->IsEmpty();
+ }
+
+// ----------------------------------------------------------------------------
+// Add a point to the sequence of points that together make up the gesture
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline void CGestureHelperImpl::AddPointL( const TPointerEvent& aEvent )
+ {
+ User::LeaveIfError( AddPoint( aEvent ) );
+ }
+
+// ----------------------------------------------------------------------------
+// Add a point to the sequence of points that together make up the gesture
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline TInt CGestureHelperImpl::AddPoint( const TPointerEvent& aEvent )
+ {
+ TPoint pos = Position ( aEvent );
+ return iGesture->AddPoint( pos, GetLastEventTime() );
+ }
+
+// ----------------------------------------------------------------------------
+// StartHoldingTimer
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::StartHoldingTimer( const TPointerEvent& aNewEvent )
+ {
+ if ( !( iGesture->IsHolding() ||
+ iGesture->IsNearHoldingPoint( Position( aNewEvent ) ) ) )
+ {
+ // restart hold timer, since pointer has moved
+ iHoldingTimer->Start();
+ // Remember the point in which holding was started
+ iGesture->SetHoldingPoint();
+ }
+ }
+
+/**
+ * Helper function that calls ContinueHolding on the pointer to TGesture
+ */
+static void ContinueHolding( TAny* aGesture )
+ {
+ static_cast< CGesture* >( aGesture )->ContinueHolding();
+ }
+
+// ----------------------------------------------------------------------------
+// Add a point to the sequence of points that together make up the gesture
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::StartHoldingL()
+ {
+ // hold & tap event is specifically filtered out. Use case: in list fast
+ // scrolling activation (e.g. enhanced coverflow), tap & hold should not
+ // start fast scroll. In addition, after long tap on start position,
+ // drag and drag & hold swiping should emit normal swipe and swipe&hold
+ // events. Therefore, tap & hold is not supported.
+ __ASSERT_DEBUG( !iGesture->IsTap() && !iPreviousTapGesture, Panic( EGesturePanicIllegalLogic ) );
+
+ // holding has just started, and gesture code should be provided to client.
+ // set gesture state so that it produces a gesture code (other than drag)
+ iGesture->StartHolding();
+
+ // create an item in the cleanup stack that will set the gesture state
+ // to holding-was-started-earlier state. NotifyL may leave, but the
+ // holding-was-started-earlier state must still be successfully set,
+ // otherwise, the holding gesture code will be sent twice
+ CleanupStack::PushL( TCleanupItem( &ContinueHolding, iGesture ) );
+
+ EmitEvent( *iGesture );
+
+ // set holding state to "post holding"
+ CleanupStack::PopAndDestroy( iGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// RecyclePreviousTapGesture
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::RecyclePreviousTapGesture( TAny* aSelf )
+ {
+ CGestureHelperImpl& self = *reinterpret_cast<CGestureHelperImpl*>( aSelf );
+ self.RecycleGesture( self.iPreviousTapGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// Emit the remainder of the previous tap event (tap + released)
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitFirstTapEvent()
+ {
+ // when this function is called, a tap has turned out to _not_ be a double tap
+ __ASSERT_DEBUG( IsDoubleTapEnabled(), Panic( EGesturePanicIllegalLogic ) );
+ __ASSERT_DEBUG( iPreviousTapGesture, Panic( EGesturePanicIllegalLogic ) );
+
+ iDoubleTapTimer->Cancel();
+ CompleteAndEmit( *iPreviousTapGesture );
+ RecycleGesture(iPreviousTapGesture);
+
+ }
+
+// ----------------------------------------------------------------------------
+// EmitStartEventL
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitStartEventL( const CGesture& aGesture )
+ {
+ CGesture* startGesture = aGesture.AsStartEventLC();
+ EmitEvent( *startGesture );
+ CleanupStack::PopAndDestroy( startGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// EmitCompletionEventsL
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::CompleteAndEmit( CGesture& aGesture )
+ {
+ aGesture.SetComplete();
+ // send gesture code if holding has not been started. If holding has
+ // been started, client has already received a "hold swipe left" e.g. event, in which
+ // case don't another "swipe left" event
+ if ( !aGesture.IsHolding() )
+ {
+ // if client leaves, the state is automatically reset.
+ // In this case the client will not get the released event
+ EmitEvent( aGesture );
+ }
+
+ // send an event that stylus was lifted
+ aGesture.SetReleased();
+ EmitEvent( aGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// EmitCancelEventL
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitCancelEvent()
+ {
+ iDoubleTapTimer->Cancel();
+
+
+ CGesture& gestureToCancel = iPreviousTapGesture ? *iPreviousTapGesture : *iGesture;
+ gestureToCancel.SetCancelled();
+ EmitEvent( gestureToCancel );
+ RecycleGesture(iPreviousTapGesture);
+
+ }
+
+// ----------------------------------------------------------------------------
+// Notify observer
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitEvent( const CGesture& aGesture )
+ {
+ // deallocation of the event is happening in CGestureEventSender::RunL()
+ TGestureEvent event;
+ event.SetCode(const_cast<CGesture&>(aGesture).Code(EAxisBoth));
+ event.SetCurrentPos(aGesture.CurrentPos());
+ event.SetDistance(aGesture.Distance());
+ event.SetStartPos(aGesture.StartPos());
+ event.SetIsHolding(aGesture.IsHolding());
+ event.SetSpeed(aGesture.Speed());
+ iEventSender->AddEvent(event);
+ }
+
+// ----------------------------------------------------------------------------
+// Return a fresh gesture from the gesture pool (pool of one gesture)
+// ----------------------------------------------------------------------------
+//
+CGesture* CGestureHelperImpl::NewGesture()
+ {
+ __ASSERT_DEBUG( iUnusedGesture, Panic( EGesturePanicIllegalLogic ) ); // pool should no be empty
+
+ iUnusedGesture->Reset();
+ CGesture* freshGesture = iUnusedGesture;
+ iUnusedGesture = NULL;
+ return freshGesture;
+ }
+
+// ----------------------------------------------------------------------------
+// Return a fresh gesture from the gesture pool (pool of one gesture)
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::RecycleGesture( CGesture*& aGesturePointer )
+ {
+ // only one object fits into the pool, and that should currently be enough
+ // one pointer must be null, one non-null
+ __ASSERT_DEBUG( !iUnusedGesture != !aGesturePointer, Panic( EGesturePanicIllegalLogic ) );
+ if ( aGesturePointer )
+ {
+ iUnusedGesture = aGesturePointer;
+ aGesturePointer = NULL;
+ }
+ }