idlehomescreen/xmluirendering/uiengine/src/xngesturehelper.cpp
branchRCL_3
changeset 20 899e4666ea9a
parent 16 1526727a5b85
parent 19 79311d856354
child 21 45cc9ca502a9
equal deleted inserted replaced
16:1526727a5b85 20:899e4666ea9a
     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 TXnGestureCode CXnGestureHelper::HandlePointerEventL( 
       
   216     const TPointerEvent& aEvent )
       
   217     {
       
   218     TXnGestureCode ret( EGestureUnknown );
       
   219     
       
   220     switch ( aEvent.iType )
       
   221         {
       
   222         case TPointerEvent::EButton1Down:
       
   223             // If no up event was received during previous gesture, cancel
       
   224             // previous event and reset state
       
   225             if ( !IsIdle() )
       
   226                 {
       
   227                 iGesture->SetCancelled();
       
   228                 // ambiguous what is the right thing when "cancel" event leaves
       
   229                 // and "start" does not. Leaving for cancel *after* "start" could
       
   230                 // be unexpected to client, as client would have handled start
       
   231                 // event successfully. Assume that leaving upon cancellation
       
   232                 // can be ignored.
       
   233                 Reset();
       
   234                 }
       
   235             // adding the first point implicitly makes the state "not idle"
       
   236             AddPointL( aEvent );
       
   237             // If AddPointL leaves, IsIdle will return EFalse for other events
       
   238             // types, hence further pointer events will be ignored.
       
   239             // Therefore, holding will NOT be started if AddPointL leaves,
       
   240             // since the callback would trigger a gesture callback, and that
       
   241             // would access an empty points array.
       
   242             iHoldingTimer->Start();
       
   243             iDirection = EGestureUnknown;
       
   244             break;
       
   245 
       
   246         case TPointerEvent::EDrag:
       
   247             // ignore the event in case not in "recording" state. this may
       
   248             // happen if holding was triggered, or client sends up event after
       
   249             // down event was received in a different *client* state, and
       
   250             // client did not forward the down event to here.
       
   251             // Also, while stylus down, the same event is received repeatedly
       
   252             // even if stylus does not move. Filter out by checking if point
       
   253             // is the same as the latest point
       
   254             if ( iDirection != EGestureCanceled )
       
   255                 {
       
   256                 if ( iDirection == EGestureUnknown )
       
   257                     {
       
   258                     // check current direction
       
   259                     iDirection = iGesture->CodeFromPoints( CXnGesture::EAxisBoth );
       
   260                     }
       
   261                 else
       
   262                     {
       
   263                     // check if direction has changed
       
   264                     if ( iDirection != iGesture->LastDirection( CXnGesture::EAxisBoth ) )
       
   265                         {
       
   266                         // cancel swipe
       
   267                         iDirection = EGestureCanceled;
       
   268                         }
       
   269                     }
       
   270                 
       
   271                 if ( !IsIdle() && !iGesture->IsLatestPoint( Position( aEvent ) ) )
       
   272                     {
       
   273                     AddPointL( aEvent );
       
   274                     if ( !( iGesture->IsHolding() ||
       
   275                             iGesture->IsNearHoldingPoint( Position( aEvent ) ) ) )
       
   276                         {
       
   277                         // restart hold timer, since pointer has moved
       
   278                         iHoldingTimer->Start();
       
   279                         // Remember the point in which holding was started
       
   280                         iGesture->SetHoldingPoint();
       
   281                         }
       
   282                     }
       
   283                 }
       
   284             break;
       
   285 
       
   286         case TPointerEvent::EButton1Up:
       
   287             // ignore up event if no down event received
       
   288             if ( !IsIdle() )
       
   289                 {
       
   290                 // reset in case the down event is not received for a reason
       
   291                 // in client, and instead drag or up events are received.
       
   292                 // reset via cleanup stack to ensure Reset is run even if
       
   293                 // observer leaves
       
   294                 CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) );
       
   295                 iGesture->SetComplete();
       
   296                 if ( iDirection != EGestureCanceled )
       
   297                     {
       
   298                     // if adding of the point fails, notify client with a
       
   299                     // cancelled event. It would be wrong to send another
       
   300                     // gesture code when the up point is not known
       
   301                     if ( AddPoint( aEvent ) != KErrNone )
       
   302                         {
       
   303                         iGesture->SetCancelled();
       
   304                         }
       
   305                     else
       
   306                         {
       
   307                         // send gesture code if holding has not been started
       
   308                         if ( !iGesture->IsHolding() )
       
   309                             {
       
   310                             // if client leaves, the state is automatically reset.
       
   311                             // In this case the client will not get the released event
       
   312                             ret = iDirection;
       
   313                             }
       
   314                         // send an event that stylus was lifted
       
   315                         iGesture->SetReleased();
       
   316                         }
       
   317                     }
       
   318                 // reset state
       
   319                 CleanupStack::PopAndDestroy( this );
       
   320                 }
       
   321             break;
       
   322 
       
   323         default:
       
   324             break;
       
   325         }
       
   326     
       
   327     return ret;
       
   328     }
       
   329 
       
   330 // ----------------------------------------------------------------------------
       
   331 // Is the helper idle?
       
   332 // inline ok in cpp file for a private member function
       
   333 // ----------------------------------------------------------------------------
       
   334 //
       
   335 inline TBool CXnGestureHelper::IsIdle() const
       
   336     {
       
   337     return iGesture->IsEmpty();
       
   338     }
       
   339 
       
   340 // ----------------------------------------------------------------------------
       
   341 // Add a point to the sequence of points that together make up the gesture
       
   342 // inline ok in cpp file for a private member function
       
   343 // ----------------------------------------------------------------------------
       
   344 //
       
   345 inline void CXnGestureHelper::AddPointL( const TPointerEvent& aEvent )
       
   346     {
       
   347     User::LeaveIfError( AddPoint( aEvent ) );
       
   348     }
       
   349 
       
   350 // ----------------------------------------------------------------------------
       
   351 // Add a point to the sequence of points that together make up the gesture
       
   352 // inline ok in cpp file for a private member function
       
   353 // ----------------------------------------------------------------------------
       
   354 //
       
   355 inline TInt CXnGestureHelper::AddPoint( const TPointerEvent& aEvent )
       
   356     {
       
   357     return iGesture->AddPoint( Position ( aEvent ) );
       
   358     }
       
   359 
       
   360 /**
       
   361 * Helper function that calls ContinueHolding on the pointer to TGesture
       
   362 */
       
   363 static void ContinueHolding( TAny* aGesture )
       
   364     {
       
   365     static_cast< CXnGesture* >( aGesture )->ContinueHolding();
       
   366     }
       
   367 
       
   368 // ----------------------------------------------------------------------------
       
   369 // Add a point to the sequence of points that together make up the gesture
       
   370 // ----------------------------------------------------------------------------
       
   371 //
       
   372 void CXnGestureHelper::StartHoldingL()
       
   373     {
       
   374     // hold & tap event is specifically filtered out. Use case: in list fast
       
   375     // scrolling activation (e.g. enhanced coverflow), tap & hold should not
       
   376     // start fast scroll. In addition, after long tap on start position,
       
   377     // drag and drag & hold swiping should emit normal swipe and swipe&hold
       
   378     // events. Therefore, tap & hold is not supported.
       
   379     if ( !iGesture->IsTap() )
       
   380         {
       
   381         // holding has just started, and gesture code should be provided to client.
       
   382         // set gesture state so that it produces a gesture code (other than drag)
       
   383         iGesture->StartHolding();
       
   384 
       
   385         // create an item in the cleanup stack that will set the gesture state
       
   386         // to holding-was-started-earlier state. NotifyL may leave, but the
       
   387         // holding-was-started-earlier state must still be successfully set,
       
   388         // otherwise, the holding gesture code will be sent twice
       
   389         CleanupStack::PushL( TCleanupItem( &ContinueHolding, iGesture ) );
       
   390 
       
   391         // set holding state to "post holding"
       
   392         CleanupStack::PopAndDestroy( iGesture );
       
   393         }
       
   394     }
       
   395 
       
   396 // ----------------------------------------------------------------------------
       
   397 // Check if swipe is valid
       
   398 // ----------------------------------------------------------------------------
       
   399 //
       
   400 TXnGestureCode CXnGestureHelper::ValidSwipe() const
       
   401     {                        
       
   402     return iGesture->Code( CXnGesture::EAxisBoth );
       
   403     }