idlehomescreen/xmluirendering/uiengine/src/xngesturehelper.cpp
changeset 0 f72a12da539e
child 16 9674c1a575e9
equal deleted inserted replaced
-1:000000000000 0:f72a12da539e
       
     1 /*
       
     2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Gesture helper implementation
       
    15 *
       
    16 */
       
    17 
       
    18 // System includes
       
    19 #include <e32base.h>
       
    20 #include <w32std.h>
       
    21 
       
    22 // User includes
       
    23 #include "xngesturehelper.h"
       
    24 #include "xngesture.h"
       
    25 #include "xngesturedefs.h"
       
    26 #include "xnnode.h"
       
    27 
       
    28 using namespace XnGestureHelper;
       
    29 
       
    30 namespace XnGestureHelper
       
    31     {
       
    32     NONSHARABLE_CLASS( CHoldingTimer ) : public CTimer
       
    33         {
       
    34     public:
       
    35         /** Two-phase constructor */
       
    36         static CHoldingTimer* NewL( CXnGestureHelper& aHelper )
       
    37             {
       
    38             CHoldingTimer* self = new ( ELeave ) CHoldingTimer( aHelper );
       
    39             CleanupStack::PushL( self );
       
    40             self->ConstructL();
       
    41             // "hold event" sending is enabled by default
       
    42             self->iIsEnabled = ETrue;
       
    43             CActiveScheduler::Add( self );
       
    44             CleanupStack::Pop( self );
       
    45             return self;
       
    46             }
       
    47 
       
    48         /** Destructor */
       
    49         ~CHoldingTimer()
       
    50             {
       
    51             Cancel();
       
    52             }
       
    53 
       
    54         /** Set whether sending holding events is currently enabled */
       
    55         void SetEnabled( TBool aEnabled )
       
    56             {
       
    57             iIsEnabled = aEnabled;
       
    58             // cancel in case hold timer is already running
       
    59             Cancel();
       
    60             }
       
    61 
       
    62         /** @return whether sending holding events is currently enabled */
       
    63         TBool IsEnabled() const
       
    64             {
       
    65             return iIsEnabled;
       
    66             }
       
    67 
       
    68         /** Start the timer. Calls CXnGestureHelper::StartHoldingL upon
       
    69           * completion */
       
    70         void Start()
       
    71             {
       
    72             // if sending hold events is disabled, do not ever start the hold
       
    73             // timer, and hence hold events will never be triggered
       
    74             if ( iIsEnabled )
       
    75                 {
       
    76                 Cancel();
       
    77                 After( KHoldDuration );
       
    78                 }
       
    79             }
       
    80 
       
    81     private:
       
    82         /** Constructor */
       
    83         CHoldingTimer( CXnGestureHelper& aHelper )
       
    84             : // give higher priority to new pointer events with - 1
       
    85               CTimer( EPriorityUserInput - 1 ),
       
    86               iHelper( aHelper )
       
    87             {
       
    88             }
       
    89 
       
    90         void RunL() // From CActive
       
    91             {
       
    92             iHelper.StartHoldingL();
       
    93             }
       
    94 
       
    95     private:
       
    96         /// helper object that will be called back when timer is triggered
       
    97         CXnGestureHelper& iHelper;
       
    98         /// whether sending holding events is currently enabled
       
    99         TBool iIsEnabled;
       
   100         };
       
   101     } // namespace GestureHelper
       
   102 
       
   103 /**
       
   104 * @return position from event. Use this instead of using aEvent direction to
       
   105 *         avoid accidentally using TPointerEvent::iPosition
       
   106 */
       
   107 inline TPoint Position( const TPointerEvent& aEvent )
       
   108     {
       
   109     // use parent position, since the capturer is using full screen area,
       
   110     // and because the (Alfred) drag events are not local to visual even when
       
   111     // coming from the client
       
   112     return aEvent.iParentPosition;
       
   113     }
       
   114 
       
   115 // ----------------------------------------------------------------------------
       
   116 // Two-phase constructor
       
   117 // ----------------------------------------------------------------------------
       
   118 //
       
   119 CXnGestureHelper* CXnGestureHelper::NewL( CXnNode& aNode )
       
   120     {
       
   121     CXnGestureHelper* self = new ( ELeave ) CXnGestureHelper( aNode );
       
   122     CleanupStack::PushL( self );
       
   123     self->iHoldingTimer = CHoldingTimer::NewL( *self );
       
   124     self->iGesture = new ( ELeave ) CXnGesture();
       
   125     CleanupStack::Pop( self );
       
   126     return self;
       
   127     }
       
   128 
       
   129 // ----------------------------------------------------------------------------
       
   130 // Constructor
       
   131 // ----------------------------------------------------------------------------
       
   132 //
       
   133 CXnGestureHelper::CXnGestureHelper( CXnNode& aNode )
       
   134     : iOwner( aNode )
       
   135     {
       
   136     }
       
   137 
       
   138 // ----------------------------------------------------------------------------
       
   139 // Destructor
       
   140 // ----------------------------------------------------------------------------
       
   141 //
       
   142 CXnGestureHelper::~CXnGestureHelper()
       
   143     {
       
   144     delete iHoldingTimer;
       
   145     delete iGesture;
       
   146     }
       
   147 
       
   148 // ----------------------------------------------------------------------------
       
   149 // SetHoldingEnabled
       
   150 // ----------------------------------------------------------------------------
       
   151 //
       
   152 void CXnGestureHelper::SetHoldingEnabled( TBool aEnabled )
       
   153     {
       
   154     iHoldingTimer->SetEnabled( aEnabled );
       
   155     }
       
   156 
       
   157 // ----------------------------------------------------------------------------
       
   158 // IsHoldingEnabled
       
   159 // ----------------------------------------------------------------------------
       
   160 //
       
   161 TBool CXnGestureHelper::IsHoldingEnabled() const
       
   162     {
       
   163     return iHoldingTimer->IsEnabled();
       
   164     }
       
   165 
       
   166 // ----------------------------------------------------------------------------
       
   167 // Reset state
       
   168 // ----------------------------------------------------------------------------
       
   169 //
       
   170 void CXnGestureHelper::Reset()
       
   171     {
       
   172     iHoldingTimer->Cancel();
       
   173     iGesture->Reset();
       
   174     }
       
   175 
       
   176 /**
       
   177 * Helper function that calls Reset on the pointer to CXnGestureHelper
       
   178 */
       
   179 static void ResetHelper( TAny* aHelper )
       
   180     {
       
   181     static_cast< CXnGestureHelper* >( aHelper )->Reset();
       
   182     }
       
   183 
       
   184 // ----------------------------------------------------------------------------
       
   185 // Sets gesture destination
       
   186 // ----------------------------------------------------------------------------
       
   187 //
       
   188 void CXnGestureHelper::SetDestination( CXnNode* aDestination )
       
   189     {
       
   190     iDestination = aDestination;
       
   191     }
       
   192 
       
   193 // ----------------------------------------------------------------------------
       
   194 // Gets gesture destination
       
   195 // ----------------------------------------------------------------------------
       
   196 //    
       
   197 CXnNode* CXnGestureHelper::Destination() const
       
   198     {
       
   199     return iDestination;
       
   200     }
       
   201 
       
   202 // ----------------------------------------------------------------------------
       
   203 // Gets gesture owner
       
   204 // ----------------------------------------------------------------------------
       
   205 //    
       
   206 CXnNode* CXnGestureHelper::Owner() const
       
   207     {
       
   208     return &iOwner;
       
   209     }
       
   210     
       
   211 // ----------------------------------------------------------------------------
       
   212 // Handle a pointer event
       
   213 // ----------------------------------------------------------------------------
       
   214 //
       
   215 TSwipeResult CXnGestureHelper::HandlePointerEventL( const TPointerEvent& aEvent )
       
   216     {
       
   217     TSwipeResult ret = ESwipeNone;
       
   218     switch ( aEvent.iType )
       
   219         {
       
   220         case TPointerEvent::EButton1Down:
       
   221             // If no up event was received during previous gesture, cancel
       
   222             // previous event and reset state
       
   223             if ( !IsIdle() )
       
   224                 {
       
   225                 iGesture->SetCancelled();
       
   226                 // ambiguous what is the right thing when "cancel" event leaves
       
   227                 // and "start" does not. Leaving for cancel *after* "start" could
       
   228                 // be unexpected to client, as client would have handled start
       
   229                 // event successfully. Assume that leaving upon cancellation
       
   230                 // can be ignored.
       
   231                 Reset();
       
   232                 }
       
   233             // adding the first point implicitly makes the state "not idle"
       
   234             AddPointL( aEvent );
       
   235             // If AddPointL leaves, IsIdle will return EFalse for other events
       
   236             // types, hence further pointer events will be ignored.
       
   237             // Therefore, holding will NOT be started if AddPointL leaves,
       
   238             // since the callback would trigger a gesture callback, and that
       
   239             // would access an empty points array.
       
   240             iHoldingTimer->Start();
       
   241             break;
       
   242 
       
   243         case TPointerEvent::EDrag:
       
   244             // ignore the event in case not in "recording" state. this may
       
   245             // happen if holding was triggered, or client sends up event after
       
   246             // down event was received in a different *client* state, and
       
   247             // client did not forward the down event to here.
       
   248             // Also, while stylus down, the same event is received repeatedly
       
   249             // even if stylus does not move. Filter out by checking if point
       
   250             // is the same as the latest point
       
   251             if ( !IsIdle() && !iGesture->IsLatestPoint( Position( aEvent ) ) )
       
   252                 {
       
   253                 AddPointL( aEvent );
       
   254                 if ( !( iGesture->IsHolding() ||
       
   255                         iGesture->IsNearHoldingPoint( Position( aEvent ) ) ) )
       
   256                     {
       
   257                     // restart hold timer, since pointer has moved
       
   258                     iHoldingTimer->Start();
       
   259                     // Remember the point in which holding was started
       
   260                     iGesture->SetHoldingPoint();
       
   261                     }
       
   262                 }
       
   263             break;
       
   264 
       
   265         case TPointerEvent::EButton1Up:
       
   266             // ignore up event if no down event received
       
   267             if ( !IsIdle() )
       
   268                 {
       
   269                 // reset in case the down event is not received for a reason
       
   270                 // in client, and instead drag or up events are received.
       
   271                 // reset via cleanup stack to ensure Reset is run even if
       
   272                 // observer leaves
       
   273                 CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) );
       
   274                 iGesture->SetComplete();
       
   275                 // if adding of the point fails, notify client with a
       
   276                 // cancelled event. It would be wrong to send another
       
   277                 // gesture code when the up point is not known
       
   278                 if ( AddPoint( aEvent ) != KErrNone )
       
   279                     {
       
   280                     iGesture->SetCancelled();
       
   281                     }
       
   282                 else
       
   283                     {
       
   284                     // send gesture code if holding has not been started
       
   285                     if ( !iGesture->IsHolding() )
       
   286                         {
       
   287                         // if client leaves, the state is automatically reset.
       
   288                         // In this case the client will not get the released event
       
   289                         ret = ValidSwipe();
       
   290                         }
       
   291                     // send an event that stylus was lifted
       
   292                     iGesture->SetReleased();
       
   293                     }
       
   294                 // reset state
       
   295                 CleanupStack::PopAndDestroy( this );
       
   296                 }
       
   297             break;
       
   298 
       
   299         default:
       
   300             break;
       
   301         }
       
   302     return ret;
       
   303     }
       
   304 
       
   305 // ----------------------------------------------------------------------------
       
   306 // Is the helper idle?
       
   307 // inline ok in cpp file for a private member function
       
   308 // ----------------------------------------------------------------------------
       
   309 //
       
   310 inline TBool CXnGestureHelper::IsIdle() const
       
   311     {
       
   312     return iGesture->IsEmpty();
       
   313     }
       
   314 
       
   315 // ----------------------------------------------------------------------------
       
   316 // Add a point to the sequence of points that together make up the gesture
       
   317 // inline ok in cpp file for a private member function
       
   318 // ----------------------------------------------------------------------------
       
   319 //
       
   320 inline void CXnGestureHelper::AddPointL( const TPointerEvent& aEvent )
       
   321     {
       
   322     User::LeaveIfError( AddPoint( aEvent ) );
       
   323     }
       
   324 
       
   325 // ----------------------------------------------------------------------------
       
   326 // Add a point to the sequence of points that together make up the gesture
       
   327 // inline ok in cpp file for a private member function
       
   328 // ----------------------------------------------------------------------------
       
   329 //
       
   330 inline TInt CXnGestureHelper::AddPoint( const TPointerEvent& aEvent )
       
   331     {
       
   332     return iGesture->AddPoint( Position ( aEvent ) );
       
   333     }
       
   334 
       
   335 /**
       
   336 * Helper function that calls ContinueHolding on the pointer to TGesture
       
   337 */
       
   338 static void ContinueHolding( TAny* aGesture )
       
   339     {
       
   340     static_cast< CXnGesture* >( aGesture )->ContinueHolding();
       
   341     }
       
   342 
       
   343 // ----------------------------------------------------------------------------
       
   344 // Add a point to the sequence of points that together make up the gesture
       
   345 // ----------------------------------------------------------------------------
       
   346 //
       
   347 void CXnGestureHelper::StartHoldingL()
       
   348     {
       
   349     // hold & tap event is specifically filtered out. Use case: in list fast
       
   350     // scrolling activation (e.g. enhanced coverflow), tap & hold should not
       
   351     // start fast scroll. In addition, after long tap on start position,
       
   352     // drag and drag & hold swiping should emit normal swipe and swipe&hold
       
   353     // events. Therefore, tap & hold is not supported.
       
   354     if ( !iGesture->IsTap() )
       
   355         {
       
   356         // holding has just started, and gesture code should be provided to client.
       
   357         // set gesture state so that it produces a gesture code (other than drag)
       
   358         iGesture->StartHolding();
       
   359 
       
   360         // create an item in the cleanup stack that will set the gesture state
       
   361         // to holding-was-started-earlier state. NotifyL may leave, but the
       
   362         // holding-was-started-earlier state must still be successfully set,
       
   363         // otherwise, the holding gesture code will be sent twice
       
   364         CleanupStack::PushL( TCleanupItem( &ContinueHolding, iGesture ) );
       
   365 
       
   366         // set holding state to "post holding"
       
   367         CleanupStack::PopAndDestroy( iGesture );
       
   368         }
       
   369     }
       
   370 
       
   371 // ----------------------------------------------------------------------------
       
   372 // Check if swipe is valid
       
   373 // ----------------------------------------------------------------------------
       
   374 //
       
   375 TSwipeResult CXnGestureHelper::ValidSwipe()
       
   376     {
       
   377     TSwipeResult ret = ESwipeNone;
       
   378     TBool validSwipe(ETrue);
       
   379 
       
   380     // check if swipe is between defined values
       
   381     TInt distanceX = Abs( iGesture->Distance().iX );
       
   382     TInt speedX = Abs( static_cast< TInt >( iGesture->Speed().iX ) );
       
   383 
       
   384     TInt minLength( iOwner.MarginRect().Width() / 2 );
       
   385     
       
   386     TInt dy( Abs( iGesture->StartPos().iY - iGesture->CurrentPos().iY ) );
       
   387     
       
   388     if ( distanceX < minLength )
       
   389         {
       
   390         validSwipe = EFalse;
       
   391         }
       
   392 
       
   393     if ( speedX < KGestureMinSpeedX )
       
   394         {
       
   395         validSwipe = EFalse;
       
   396         }
       
   397                
       
   398     if ( dy > KGestureMaxDeltaY )
       
   399         {
       
   400         validSwipe = EFalse;
       
   401         }
       
   402     
       
   403     // check the direction of swipe
       
   404     if ( validSwipe )
       
   405         {
       
   406         switch ( iGesture->Code( CXnGesture::EAxisHorizontal ) )
       
   407             {
       
   408              case EGestureSwipeLeft:
       
   409                 ret = ESwipeLeft;
       
   410                 break;
       
   411              case EGestureSwipeRight:
       
   412                  ret = ESwipeRight;
       
   413                 break;
       
   414              default: // fall through
       
   415                 break;
       
   416             }
       
   417         }
       
   418 
       
   419     return ret;
       
   420     }