--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/idlehomescreen/xmluirendering/uiengine/src/xngesturehelper.cpp Thu Dec 17 08:40:49 2009 +0200
@@ -0,0 +1,420 @@
+/*
+* 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
+*
+*/
+
+// System includes
+#include <e32base.h>
+#include <w32std.h>
+
+// User includes
+#include "xngesturehelper.h"
+#include "xngesture.h"
+#include "xngesturedefs.h"
+#include "xnnode.h"
+
+using namespace XnGestureHelper;
+
+namespace XnGestureHelper
+ {
+ NONSHARABLE_CLASS( CHoldingTimer ) : public CTimer
+ {
+ public:
+ /** Two-phase constructor */
+ static CHoldingTimer* NewL( CXnGestureHelper& aHelper )
+ {
+ CHoldingTimer* self = new ( ELeave ) CHoldingTimer( aHelper );
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ // "hold event" sending is enabled by default
+ self->iIsEnabled = ETrue;
+ CActiveScheduler::Add( self );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+ /** Destructor */
+ ~CHoldingTimer()
+ {
+ 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 CXnGestureHelper::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( KHoldDuration );
+ }
+ }
+
+ private:
+ /** Constructor */
+ CHoldingTimer( CXnGestureHelper& aHelper )
+ : // give higher priority to new pointer events with - 1
+ CTimer( EPriorityUserInput - 1 ),
+ iHelper( aHelper )
+ {
+ }
+
+ void RunL() // From CActive
+ {
+ iHelper.StartHoldingL();
+ }
+
+ private:
+ /// helper object that will be called back when timer is triggered
+ CXnGestureHelper& iHelper;
+ /// 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
+// ----------------------------------------------------------------------------
+//
+CXnGestureHelper* CXnGestureHelper::NewL( CXnNode& aNode )
+ {
+ CXnGestureHelper* self = new ( ELeave ) CXnGestureHelper( aNode );
+ CleanupStack::PushL( self );
+ self->iHoldingTimer = CHoldingTimer::NewL( *self );
+ self->iGesture = new ( ELeave ) CXnGesture();
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+// ----------------------------------------------------------------------------
+// Constructor
+// ----------------------------------------------------------------------------
+//
+CXnGestureHelper::CXnGestureHelper( CXnNode& aNode )
+ : iOwner( aNode )
+ {
+ }
+
+// ----------------------------------------------------------------------------
+// Destructor
+// ----------------------------------------------------------------------------
+//
+CXnGestureHelper::~CXnGestureHelper()
+ {
+ delete iHoldingTimer;
+ delete iGesture;
+ }
+
+// ----------------------------------------------------------------------------
+// SetHoldingEnabled
+// ----------------------------------------------------------------------------
+//
+void CXnGestureHelper::SetHoldingEnabled( TBool aEnabled )
+ {
+ iHoldingTimer->SetEnabled( aEnabled );
+ }
+
+// ----------------------------------------------------------------------------
+// IsHoldingEnabled
+// ----------------------------------------------------------------------------
+//
+TBool CXnGestureHelper::IsHoldingEnabled() const
+ {
+ return iHoldingTimer->IsEnabled();
+ }
+
+// ----------------------------------------------------------------------------
+// Reset state
+// ----------------------------------------------------------------------------
+//
+void CXnGestureHelper::Reset()
+ {
+ iHoldingTimer->Cancel();
+ iGesture->Reset();
+ }
+
+/**
+* Helper function that calls Reset on the pointer to CXnGestureHelper
+*/
+static void ResetHelper( TAny* aHelper )
+ {
+ static_cast< CXnGestureHelper* >( aHelper )->Reset();
+ }
+
+// ----------------------------------------------------------------------------
+// Sets gesture destination
+// ----------------------------------------------------------------------------
+//
+void CXnGestureHelper::SetDestination( CXnNode* aDestination )
+ {
+ iDestination = aDestination;
+ }
+
+// ----------------------------------------------------------------------------
+// Gets gesture destination
+// ----------------------------------------------------------------------------
+//
+CXnNode* CXnGestureHelper::Destination() const
+ {
+ return iDestination;
+ }
+
+// ----------------------------------------------------------------------------
+// Gets gesture owner
+// ----------------------------------------------------------------------------
+//
+CXnNode* CXnGestureHelper::Owner() const
+ {
+ return &iOwner;
+ }
+
+// ----------------------------------------------------------------------------
+// Handle a pointer event
+// ----------------------------------------------------------------------------
+//
+TSwipeResult CXnGestureHelper::HandlePointerEventL( const TPointerEvent& aEvent )
+ {
+ TSwipeResult ret = ESwipeNone;
+ switch ( aEvent.iType )
+ {
+ case TPointerEvent::EButton1Down:
+ // If no up event was received during previous gesture, cancel
+ // previous event and reset state
+ if ( !IsIdle() )
+ {
+ iGesture->SetCancelled();
+ // 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.
+ Reset();
+ }
+ // adding the first point implicitly makes the state "not idle"
+ AddPointL( aEvent );
+ // If AddPointL leaves, IsIdle will return EFalse for other events
+ // types, hence further pointer events will be ignored.
+ // Therefore, holding will NOT be started if AddPointL leaves,
+ // since the callback would trigger a gesture callback, and that
+ // would access an empty points array.
+ iHoldingTimer->Start();
+ break;
+
+ case TPointerEvent::EDrag:
+ // ignore the event in case not in "recording" state. this may
+ // happen if holding was triggered, or client sends up event after
+ // down event was received in a different *client* state, and
+ // client did not forward the down event to here.
+ // Also, 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 ( !IsIdle() && !iGesture->IsLatestPoint( Position( aEvent ) ) )
+ {
+ AddPointL( aEvent );
+ if ( !( iGesture->IsHolding() ||
+ iGesture->IsNearHoldingPoint( Position( aEvent ) ) ) )
+ {
+ // restart hold timer, since pointer has moved
+ iHoldingTimer->Start();
+ // Remember the point in which holding was started
+ iGesture->SetHoldingPoint();
+ }
+ }
+ break;
+
+ case TPointerEvent::EButton1Up:
+ // ignore up event if no down event received
+ if ( !IsIdle() )
+ {
+ // reset in case the down event 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 ) );
+ iGesture->SetComplete();
+ // 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 ( AddPoint( aEvent ) != KErrNone )
+ {
+ iGesture->SetCancelled();
+ }
+ else
+ {
+ // send gesture code if holding has not been started
+ if ( !iGesture->IsHolding() )
+ {
+ // if client leaves, the state is automatically reset.
+ // In this case the client will not get the released event
+ ret = ValidSwipe();
+ }
+ // send an event that stylus was lifted
+ iGesture->SetReleased();
+ }
+ // reset state
+ CleanupStack::PopAndDestroy( this );
+ }
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+ }
+
+// ----------------------------------------------------------------------------
+// Is the helper idle?
+// inline ok in cpp file for a private member function
+// ----------------------------------------------------------------------------
+//
+inline TBool CXnGestureHelper::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 CXnGestureHelper::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 CXnGestureHelper::AddPoint( const TPointerEvent& aEvent )
+ {
+ return iGesture->AddPoint( Position ( aEvent ) );
+ }
+
+/**
+* Helper function that calls ContinueHolding on the pointer to TGesture
+*/
+static void ContinueHolding( TAny* aGesture )
+ {
+ static_cast< CXnGesture* >( aGesture )->ContinueHolding();
+ }
+
+// ----------------------------------------------------------------------------
+// Add a point to the sequence of points that together make up the gesture
+// ----------------------------------------------------------------------------
+//
+void CXnGestureHelper::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.
+ if ( !iGesture->IsTap() )
+ {
+ // 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 ) );
+
+ // set holding state to "post holding"
+ CleanupStack::PopAndDestroy( iGesture );
+ }
+ }
+
+// ----------------------------------------------------------------------------
+// Check if swipe is valid
+// ----------------------------------------------------------------------------
+//
+TSwipeResult CXnGestureHelper::ValidSwipe()
+ {
+ TSwipeResult ret = ESwipeNone;
+ TBool validSwipe(ETrue);
+
+ // check if swipe is between defined values
+ TInt distanceX = Abs( iGesture->Distance().iX );
+ TInt speedX = Abs( static_cast< TInt >( iGesture->Speed().iX ) );
+
+ TInt minLength( iOwner.MarginRect().Width() / 2 );
+
+ TInt dy( Abs( iGesture->StartPos().iY - iGesture->CurrentPos().iY ) );
+
+ if ( distanceX < minLength )
+ {
+ validSwipe = EFalse;
+ }
+
+ if ( speedX < KGestureMinSpeedX )
+ {
+ validSwipe = EFalse;
+ }
+
+ if ( dy > KGestureMaxDeltaY )
+ {
+ validSwipe = EFalse;
+ }
+
+ // check the direction of swipe
+ if ( validSwipe )
+ {
+ switch ( iGesture->Code( CXnGesture::EAxisHorizontal ) )
+ {
+ case EGestureSwipeLeft:
+ ret = ESwipeLeft;
+ break;
+ case EGestureSwipeRight:
+ ret = ESwipeRight;
+ break;
+ default: // fall through
+ break;
+ }
+ }
+
+ return ret;
+ }