--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mulwidgets/gesturehelper/src/gesturehelperimpl.cpp Thu Dec 17 08:56:02 2009 +0200
@@ -0,0 +1,898 @@
+/*
+* 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 "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 <alf/alfevent.h>
+#include <e32base.h>
+#include <w32std.h>
+
+#include "gesture.h"
+#include "gesturedefs.h"
+
+#include "pointercapturer.h"
+
+using namespace GestureHelper;
+
+namespace 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 );
+ }
+ }
+
+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.iParentPosition;
+ }
+
+// ----------------------------------------------------------------------------
+// Two-phase constructor
+// ----------------------------------------------------------------------------
+//
+CGestureHelperImpl* CGestureHelperImpl::NewL( MGestureObserver& aObserver )
+ {
+ CGestureHelperImpl* self = new ( ELeave ) CGestureHelperImpl( );
+ CleanupStack::PushL( self );
+ self->iDoubleTapTimer = CCallbackTimer::NewL( *self, EmitFirstTapEventL,
+ KMaxDoubleTapDuration, EFalse ); // double tap is disabled by default
+ self->iHoldingTimer = CCallbackTimer::NewL( *self, StartHoldingL,
+ KHoldDuration, ETrue ); // holding is enabled by default
+ self->iGesture = new ( ELeave ) CGesture();
+ self->iUnusedGesture = new ( ELeave ) CGesture();
+ self->iPointerCapturer = CPointerCapturer::NewL();
+ self->iPointerCount = 0;
+ self->iObserver = &aObserver;
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+// ----------------------------------------------------------------------------
+// Destructor
+// ----------------------------------------------------------------------------
+//
+CGestureHelperImpl::~CGestureHelperImpl()
+ {
+ delete iDoubleTapTimer;
+ delete iHoldingTimer;
+ delete iGesture;
+ delete iPreviousTapGesture;
+ delete iUnusedGesture;
+ delete iPointerCapturer;
+ }
+
+// ----------------------------------------------------------------------------
+// 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();
+ }
+
+// ----------------------------------------------------------------------------
+// InitAlfredPointerEventCaptureL
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::InitAlfredPointerCaptureL( CAlfEnv& aEnv,
+ CAlfDisplay& aDisplay, TInt aFreeControlGroupId )
+ {
+ iPointerCapturer->InitForAlfredL(*this, aEnv, aDisplay, aFreeControlGroupId );
+ }
+
+// ----------------------------------------------------------------------------
+// Reset state
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::Reset()
+ {
+ iHoldingTimer->Cancel();
+ iGesture->Reset();
+ iPointerCapturer->Stop();
+ iPointerCount=0;
+ iCurrentPointer = -1;
+ }
+
+/**
+ * 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 )
+ {
+ return HandlePointerEventL( aEvent, NULL );
+ }
+
+// ----------------------------------------------------------------------------
+// OfferEventL
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::OfferEventL( const TAlfEvent& aEvent )
+ {
+ if ( aEvent.IsPointerEvent() )
+ {
+ return HandlePointerEventL( aEvent.PointerEvent(), aEvent.Visual() );
+ }
+ return EFalse;
+ }
+
+// ----------------------------------------------------------------------------
+// Handle a pointer event
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::HandlePointerEventL( const TPointerEvent& aEvent,
+ CAlfVisual* aVisual )
+ {
+ TInt pointerNumber = GestureHelper::TGestureRecogniser().PointerNumber( aEvent );
+
+ if( !ValidatePointer( aEvent, pointerNumber ) )
+ {
+ return EFalse; // don't consume
+ }
+
+ switch ( aEvent.iType )
+ {
+ case TPointerEvent::EButton1Down:
+ // Error handling for recieveing a button down on the pointer which
+ // is already touched down
+ // Here the pointer number shouldnot be considered for validtaing.
+ // We should consider which pointer is currently touched down
+ // It can so happen that user has touched was doing pinch and then released his
+ // 1st finger. and starts to do panning with the second finger still touched down.
+ // Then again when he touches the first finger, in that case you should be validating these pointers w.r.t
+ // the pointer numbers of the last touched finger
+
+ // we shouldnot consider pointer number here. If pointer 1 is
+ // already touched and dragging and we recieve a down of pointer 0
+
+ if( iPointerCount == 1 )
+ {
+ if(iCurrentPointer == pointerNumber )
+ {
+ TRAP_IGNORE( EmitCancelEventL() );
+ Reset();
+ }
+ }
+ else if( iPointerCount == 2 )
+ {
+ // if pointer count is already 2, then reset the array of pointer for
+ // which a down event is recieved, and continue handling in normal way
+ iPointerCount = 1;
+ iCurrentPointer = pointerNumber == 0 ? 1 : 0;
+ iGesture->ResetToLastPoint(pointerNumber != 0,pointerNumber != 0);
+ }
+
+ if(iPointerCount == 0)
+ {
+ iPointerCount = 1;
+ iCurrentPointer = pointerNumber;
+ // single touch gesture start
+ iGesture->SetSingleTouchActive();
+ HandleSinglePointerEventL( aEvent, aVisual );
+ }
+ else if(iPointerCount == 1)
+ {
+ iPointerCount = 2;
+ iCurrentPointer = -1;
+ // add the last point of the single touch event
+ // to first array of gesture
+ iGesture->ResetToLastPoint(pointerNumber != 0,ETrue);
+ iGesture->SetMultiTouchActive();
+ // multi touch gesture start
+ HandleMultiplePointerEventL( aEvent, pointerNumber );
+ }
+ else
+ {
+
+ }
+ break;
+
+ case TPointerEvent::EDrag:
+ if(iPointerCount == 1)
+ {
+ HandleSinglePointerEventL( aEvent, aVisual );
+ }
+ else if(iPointerCount == 2)
+ {
+ HandleMultiplePointerEventL( aEvent, pointerNumber );
+ }
+ else
+ {
+ // nothing to be done
+ }
+ break;
+
+ case TPointerEvent::EButton1Up:
+ if(iPointerCount == 2)
+ {
+ // multi touch gesture complete
+ HandleMultiplePointerEventL( aEvent, pointerNumber );
+ // should the pointer count decrese first n then
+ // handling of event or otherwise
+ iPointerCount = 1;
+ iCurrentPointer = pointerNumber == 0 ? 1 : 0;
+ iGesture->ResetToLastPoint(ETrue,pointerNumber != 0);
+ iGesture->SetSingleTouchActive();
+ }
+ else if( iPointerCount == 1 )
+ {
+ iPointerCount = 0;
+ iCurrentPointer = -1;
+ // single touch gesture complete
+ HandleSinglePointerEventL( aEvent, aVisual );
+ }
+ else
+ {
+ // nothing to be done
+ }
+ break;
+
+ default:
+ break;
+ }
+ return ETrue; // consume
+ }
+// ----------------------------------------------------------------------------
+// Handle a pointer event
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::HandleSinglePointerEventL( const TPointerEvent& aEvent,
+ CAlfVisual* aVisual )
+ {
+ switch ( aEvent.iType )
+ {
+ case TPointerEvent::EButton1Down:
+ // If no up event was received during previous gesture, cancel
+ // previous event and reset state
+ if ( !IsIdle() )
+ {
+ // ambiguous what is the right thing when "cancel" event leaves
+ // and "start" does not. Leaving for cancel *after* "start" could
+ // be unexpected to client, as client would have handled start
+ // event successfully. Assume that leaving upon cancellation
+ // can be ignored.
+ TRAP_IGNORE( EmitCancelEventL() );
+ Reset();
+ }
+ // as long as down event of a double tap comes within the double
+ // tap timeout, it does not matter how long the user keeps the finger
+ // pressed for the gesture to be a double tap. Therefore, cancel
+ // the timeout, as it is no longer relevant. (Of course, this call
+ // will only do something if the timer is actually running, which
+ // is only if received a tap event very recently.)
+ iDoubleTapTimer->Cancel();
+ // adding the first point implicitly makes the state "not idle"
+ AddPointL( aEvent );
+ iGesture->SetVisual( aVisual );
+ // if pointer capturer leaves, the remaining pointer events will
+ // not be captured if stylus is dragged outside the capturing visual
+ // an error note will be shown, so the potential problem is irrelevant,
+ // assuming client does not (incorrectly) block the leave from reaching
+ // the framework
+ iPointerCapturer->StartL();
+ // Delay emitting a down event _until_ it is known that this beginning
+ // gesture is _not_ the second tap of a double tap event.
+ // iPreviousTapGesture is only non-null if very recently received
+ // a tap event and double tap is enabled.
+ if ( !iPreviousTapGesture )
+ {
+ EmitEventL( *iGesture );
+ }
+ // else delay emitting an event, as it might be a double tap
+ // (allow the second tap of a double tap to be anywhere, so don't check
+ // for start pos here)
+ break;
+
+ case TPointerEvent::EDrag:
+ // While stylus down, the same event is received repeatedly
+ // even if stylus does not move. Filter out by checking if point
+ // is the same as the latest point
+ if ( !iGesture->IsLatestPoint( Position( aEvent ) ) )
+ {
+ AddPointL( aEvent );
+
+ // as long as the starting gesture is seen as a tap, do not emit any
+ // drag events
+ if ( !iGesture->IsTap() )
+ {
+ // if there is a previous tap gesture, getting drag events means that
+ // the previous gesture is not a double tap. So emit the previous gesture.
+ if ( iPreviousTapGesture )
+ {
+ // this is a second gesture after a tap (double tap is enabled)
+ EmitFirstTapEventL();
+ // emit down event for the current gesture (since its down was delayed, until
+ // it was to be known if the event is a tap. That is known now.)
+ EmitStartEventL( *iGesture );
+ }
+ // restart holding timer every time the current stylus pos changes
+ StartHoldingTimer( aEvent );
+ // emit the drag event to client
+ EmitEventL( *iGesture );
+ }
+ // else: do not emit drag events until it is known that the gesture is not a tap
+ // (or the second tap of double tap)
+ }
+ break;
+
+ case TPointerEvent::EButton1Up:
+ // reset in case the down event for next gesture is not received for a reason
+ // in client, and instead drag or up events are received.
+ // reset via cleanup stack to ensure Reset is run even if
+ // observer leaves
+ CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) );
+ // if adding of the point fails, notify client with a
+ // cancelled event. It would be wrong to send another
+ // gesture code when the up point is not known
+ if ( KErrNone == AddPoint( aEvent ) )
+ {
+ // if the gesture is a tap, the gesture is either the first tap of a _potential_
+ // double tap, or the second tap of a double tap
+ if ( iDoubleTapTimer->IsEnabled() && iGesture->IsTap() )
+ {
+ __ASSERT_DEBUG( !iGesture->IsHolding(), Panic( EGesturePanicIllegalLogic ) );
+ if ( !iPreviousTapGesture )
+ {
+ // First tap. Delay emitting its code evemt and released events until it is known
+ // whether the tap is a double tap
+ iPreviousTapGesture = iGesture;
+ iGesture = NewGesture();
+ iDoubleTapTimer->Start();
+ }
+ else
+ {
+ // This is a second tap of a double tap. Do not emit anything for the second
+ // tap. Only down event has been emitted for the first tap. Emit the code
+ // event (double tap) and released for the first tap.
+ iPreviousTapGesture->SetDoubleTap();
+ EmitFirstTapEventL();
+ }
+ }
+ else
+ {
+ // modified iGesture to be "released"
+ CompleteAndEmitSingleTouchL( *iGesture );
+ }
+ }
+ else
+ { // adding a point failed
+ EmitCancelEventL();
+ }
+ // reset state
+ CleanupStack::PopAndDestroy( this );
+ break;
+
+ default:
+ break;
+ }
+ return ETrue; // consume
+ }
+
+// ----------------------------------------------------------------------------
+// Handle multiple pointer events
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::HandleMultiplePointerEventL( const TPointerEvent& aEvent,
+ const TInt aPointerNumber )
+ {
+ switch ( aEvent.iType )
+ {
+ case TPointerEvent::EButton1Down:
+ // adding the first point implicitly makes the state "not idle"
+ AddMultiTouchPointsL( aEvent, aPointerNumber);
+ EmitEventL( *iGesture );
+ break;
+
+ case TPointerEvent::EDrag:
+ // While stylus down, the same event is received repeatedly
+ // even if stylus does not move. Filter out by checking if point
+ // is the same as the latest point
+ if( AddMultiTouchPointsL(aEvent, aPointerNumber ))
+ {
+ // as long as the starting gesture is seen as a tap, do not emit any
+ // drag events
+ if ( iGesture->IsPinch() )
+ {
+ // emit the pinch event to client
+ EmitEventL( *iGesture );
+ }
+ // else: do not emit any events to the client
+ }
+
+
+ break;
+
+ case TPointerEvent::EButton1Up:
+
+ // if adding of the point fails, notify client with a
+ // cancelled event. It would be wrong to send another
+ // gesture code when the up point is not known
+ AddMultiTouchPointsL(aEvent, aPointerNumber );
+ CompleteAndEmitDoubleTouchL( *iGesture );
+
+ break;
+
+ default:
+ break;
+ }
+ return ETrue; // consume
+ }
+
+// ----------------------------------------------------------------------------
+// Validate AddMultiTouchPointsL events
+// Check if the point recieved is the repeavt event of previous point or a new point
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline TBool CGestureHelperImpl::AddMultiTouchPointsL( const TPointerEvent& aEvent, const TInt aPointerNumber)
+ {
+ TBool pointAdded = EFalse;
+ if ( aPointerNumber > 0 )
+ {
+ if ( !iGesture->IsLatestSecondaryPoint( Position( aEvent ) ) )
+ {
+ AddSecondaryPointL( aEvent );
+ pointAdded = ETrue;
+ }
+ }
+ else
+ {
+ if ( !iGesture->IsLatestPoint( Position( aEvent ) ) )
+ {
+ AddPointL( aEvent );
+ pointAdded = ETrue;
+ }
+ }
+ return pointAdded;
+ }
+
+// ----------------------------------------------------------------------------
+// Check for Stray evnet
+// ----------------------------------------------------------------------------
+//
+TBool CGestureHelperImpl::StrayEvent( const TPointerEvent& aEvent ) const
+ {
+ //If we are recieving a button down on pointer 0 in pointer capturer then its a stray event
+ // Dont consume it
+ if ( aEvent.iType == TPointerEvent::EButton1Down && IsIdle( 0) )
+ {
+ return ETrue; // don't consume
+ }
+ return EFalse;
+ }
+// ----------------------------------------------------------------------------
+// Validate the events
+// is it a valid event or a stray pointer form some other visuals
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline TBool CGestureHelperImpl::ValidatePointer( const TPointerEvent& aEvent, const TInt aPointerNumber) const
+ {
+ // Check if received event is valid or not.
+ // In practice, event is NOT valid in the following situations:
+ //
+ // 1. Pointer down event is received for pointer which is already down.
+ // 2. Pointer up event is received for pointer which is already up.
+ // 3. Pointer drag event is received for pointer which is not down.
+ // 4. Pointer numbers other than 0 and 1. We are handling only 2 pointers.
+ //
+ // In these situations this function returns EFalse,
+ // corresponding event is ignored and recognition continues as earlier.
+ //
+
+ // filter all the events for which the pointer number is less than 0
+ // or greater than 1. we will handle only 2 pointer events.
+ if( aPointerNumber >= 2 || aPointerNumber < 0 )
+ {
+ return EFalse;
+ }
+
+ // filter out events that do not start with button down. It is a stray
+ // event from another visual
+ TInt pointerTovalidate = aPointerNumber ;
+ if( iPointerCount == 1 && aPointerNumber == 1)
+ {
+ pointerTovalidate = 0;
+ }
+ if ( aEvent.iType != TPointerEvent::EButton1Down && IsIdle( pointerTovalidate ) )
+ {
+ return EFalse; // don't consume
+ }
+
+
+ return ETrue;
+ }
+
+// ----------------------------------------------------------------------------
+// Is the helper idle?
+// Checks whether any points are already added in the array corresponding
+// to the pointer number
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline TBool CGestureHelperImpl::IsIdle( TBool aPointerNumber ) const
+ {
+ if( aPointerNumber == 0 )
+ {
+ return iGesture->IsEmpty();
+ }
+ else if( aPointerNumber == 1 )
+ {
+ return iGesture->IsMultiTouch();
+ }
+ return ETrue;
+ }
+
+// ----------------------------------------------------------------------------
+// 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 )
+ {
+ return iGesture->AddPoint( Position ( 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 void CGestureHelperImpl::AddSecondaryPointL( const TPointerEvent& aEvent )
+ {
+ User::LeaveIfError( AddSecondaryPoint( 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::AddSecondaryPoint( const TPointerEvent& aEvent )
+ {
+ return iGesture->AddSecondaryPoint( Position ( aEvent ) );
+ }
+
+// ----------------------------------------------------------------------------
+// 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 ) );
+
+ EmitEventL( *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::EmitFirstTapEventL()
+ {
+ // 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();
+
+ // ensure previous tap gesture is reset even if client leaves
+ CleanupStack::PushL( TCleanupItem( &RecyclePreviousTapGesture, this ) );
+
+ CompleteAndEmitSingleTouchL( *iPreviousTapGesture );
+
+ // recycle the emitted gesture
+ CleanupStack::PopAndDestroy( this );
+ }
+
+// ----------------------------------------------------------------------------
+// EmitStartEventL
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitStartEventL( const CGesture& aGesture )
+ {
+ CGesture* startGesture = aGesture.AsStartEventLC();
+ EmitEventL( *startGesture );
+ CleanupStack::PopAndDestroy( startGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// EmitCompletionEvents for single touch
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::CompleteAndEmitSingleTouchL( CGesture& aGesture )
+ {
+ aGesture.SetSingleTouchComplete();
+ // 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
+ EmitEventL( aGesture );
+ }
+ // send an event that stylus was lifted
+ aGesture.SetSingleTouchReleased();
+ EmitEventL( aGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// EmitCompletionEvents for double touch
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::CompleteAndEmitDoubleTouchL( CGesture& aGesture )
+ {
+ aGesture.SetMultiTouchComplete();
+ if ( aGesture.IsPinch() )
+ {
+ // emit the pinch event to client
+ EmitEventL( aGesture );
+ }
+ // send an event that stylus was lifted
+ aGesture.SetMultiTouchReleased();
+ EmitEventL( aGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// EmitCancelEventL
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitCancelEventL()
+ {
+ iDoubleTapTimer->Cancel();
+
+ // ensure previous tap gesture is reset even if client leaves
+ CleanupStack::PushL( TCleanupItem( &RecyclePreviousTapGesture, this ) );
+
+ CGesture& gestureToCancel = iPreviousTapGesture ? *iPreviousTapGesture : *iGesture;
+ gestureToCancel.SetCancelled();
+ EmitEventL( gestureToCancel );
+
+ // recycle the emitted gesture
+ CleanupStack::PopAndDestroy( this );
+ }
+
+// ----------------------------------------------------------------------------
+// Notify observer
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::EmitEventL( const CGesture& aGesture )
+ {
+ // iPoints array must have content when this function is called
+ iObserver->HandleGestureL( aGesture );
+ }
+
+// ----------------------------------------------------------------------------
+// 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;
+ }
+ }
+
+// ----------------------------------------------------------------------------
+// AddObserver
+// ----------------------------------------------------------------------------
+//
+void CGestureHelperImpl::AddObserver(MGestureObserver* aObserver)
+ {
+ iObserver = aObserver;
+ }
+
+// end of file
+