idlehomescreen/xmluirendering/uiengine/src/xngesture.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-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 class
       
    15 *
       
    16 */
       
    17 
       
    18 #include <e32math.h>
       
    19 
       
    20 #include "xngesture.h"
       
    21 #include "xngesturedefs.h"
       
    22 
       
    23 using namespace XnGestureHelper;
       
    24 
       
    25 // ======== LOCAL FUNCTIONS ===================================================
       
    26 
       
    27 /**
       
    28  * Point array for which only x axis is relevant
       
    29  */
       
    30 class TXAxisPointArray : public TXnPointArray
       
    31     {
       
    32 public:
       
    33     TXAxisPointArray( const RArray< TXnPointEntry >& aPoints )
       
    34         : TXnPointArray( aPoints )
       
    35         {
       
    36         }
       
    37 
       
    38     // from TXnPointArray
       
    39     TPoint operator[]( TInt aIndex ) const
       
    40         {
       
    41         return TPoint( Raw( aIndex ).iX, 0 );
       
    42         }
       
    43     };
       
    44 
       
    45 /**
       
    46  * Point array for which only y axis is relevant
       
    47  */
       
    48 class TYAxisPointArray : public TXnPointArray
       
    49     {
       
    50 public:
       
    51     TYAxisPointArray( const RArray< TXnPointEntry >& aPoints )
       
    52         : TXnPointArray( aPoints )
       
    53         {
       
    54         }
       
    55 
       
    56     // from TXnPointArray
       
    57     TPoint operator[]( TInt aIndex ) const
       
    58         {
       
    59         return TPoint( 0, Raw( aIndex ).iY );
       
    60         }
       
    61     };
       
    62 
       
    63 /** @return the current time */
       
    64 TTime CurrentTime()
       
    65     {
       
    66     TTime time;
       
    67     time.UniversalTime();
       
    68     return time;
       
    69     }
       
    70 
       
    71 // ----------------------------------------------------------------------------
       
    72 // destructor
       
    73 // ----------------------------------------------------------------------------
       
    74 //
       
    75 CXnGesture::~CXnGesture()
       
    76     {
       
    77     iPoints.Close();
       
    78     }
       
    79 
       
    80 // ----------------------------------------------------------------------------
       
    81 // Reset
       
    82 // ----------------------------------------------------------------------------
       
    83 //
       
    84 void CXnGesture::Reset()
       
    85     {
       
    86     // store previous gesture data before resetting the state
       
    87     if ( iPoints.Count() > 0 )
       
    88         {
       
    89         iPreviousGesture = TGestureRecord( Type(), iCompletionTime,
       
    90             iPoints[iPoints.Count() - 1].iPos );
       
    91         }
       
    92     else
       
    93         {
       
    94         iPreviousGesture = TGestureRecord();
       
    95         }
       
    96 
       
    97     iPoints.Reset();
       
    98     iHoldingState = ENotHolding;
       
    99     iState = ENotComplete;
       
   100     iHoldingPointIndex = 0;
       
   101     }
       
   102 
       
   103 // ----------------------------------------------------------------------------
       
   104 // Reset
       
   105 // ----------------------------------------------------------------------------
       
   106 //
       
   107 TBool CXnGesture::IsEmpty() const
       
   108     {
       
   109     return iPoints.Count() == 0;
       
   110     }
       
   111 
       
   112 // ----------------------------------------------------------------------------
       
   113 // Add a point to the sequence of points that together make up the gesture
       
   114 // ----------------------------------------------------------------------------
       
   115 //
       
   116 TInt CXnGesture::AddPoint( const TPoint& aPoint )
       
   117     {
       
   118     if ( !IsLatestPoint( aPoint ) )
       
   119         {
       
   120         return iPoints.Append( TXnPointEntry( aPoint, CurrentTime() ) );
       
   121         }
       
   122     return KErrNone;
       
   123     }
       
   124 
       
   125 /**
       
   126  * @return ETrue if the point is within a specified distance of the other point
       
   127  */
       
   128 inline TBool IsNear( const TPoint& aPointUnderTest, const TPoint& aPoint,
       
   129         TInt aMargin )
       
   130     {
       
   131     TRect rect(
       
   132         aPoint.iX - aMargin, aPoint.iY - aMargin,
       
   133         aPoint.iX + aMargin, aPoint.iY + aMargin );
       
   134     return rect.Contains( aPointUnderTest );
       
   135     }
       
   136 
       
   137 // ----------------------------------------------------------------------------
       
   138 // IsNearHoldingPoint
       
   139 // ----------------------------------------------------------------------------
       
   140 //
       
   141 TBool CXnGesture::IsNearHoldingPoint( const TPoint& aPoint ) const
       
   142     {
       
   143     return IsNear( aPoint, iPoints[iHoldingPointIndex].iPos,
       
   144         KSamePointTolerance );
       
   145     }
       
   146 
       
   147 // ----------------------------------------------------------------------------
       
   148 // IsLatestPoint
       
   149 // ----------------------------------------------------------------------------
       
   150 //
       
   151 TBool CXnGesture::IsLatestPoint( const TPoint& aPoint ) const
       
   152     {
       
   153     if ( iPoints.Count() > 0 )
       
   154         {
       
   155         return aPoint == CurrentPos();
       
   156         }
       
   157     return EFalse;
       
   158     }
       
   159 
       
   160 // ----------------------------------------------------------------------------
       
   161 // StartHolding
       
   162 // ----------------------------------------------------------------------------
       
   163 //
       
   164 void CXnGesture::StartHolding()
       
   165     {
       
   166     iHoldingState = EHoldStarting;
       
   167 
       
   168     // remove all points that were introduced after holding started
       
   169     for ( TInt i = iPoints.Count() - 1; i > iHoldingPointIndex; i-- )
       
   170         {
       
   171         iPoints.Remove( i );
       
   172         }
       
   173     }
       
   174 
       
   175 // ----------------------------------------------------------------------------
       
   176 // SetHoldingPoint
       
   177 // ----------------------------------------------------------------------------
       
   178 //
       
   179 void CXnGesture::SetHoldingPoint()
       
   180     {
       
   181     iHoldingPointIndex = iPoints.Count() - 1;
       
   182     }
       
   183 
       
   184 // ----------------------------------------------------------------------------
       
   185 // ContinueHolding
       
   186 // ----------------------------------------------------------------------------
       
   187 //
       
   188 void CXnGesture::ContinueHolding()
       
   189     {
       
   190     iHoldingState = EHolding;
       
   191     }
       
   192 
       
   193 // ----------------------------------------------------------------------------
       
   194 // SetReleased
       
   195 // ----------------------------------------------------------------------------
       
   196 //
       
   197 void CXnGesture::SetReleased()
       
   198     {
       
   199     // IsMovementStopped expects SetComplete to be called before SetRelea
       
   200     __ASSERT_DEBUG( EComplete == iState, Panic( EGesturePanicIllegalLogic ) );
       
   201     iState = EReleased;
       
   202     }
       
   203 
       
   204 /**
       
   205  * @return elapsed time between aStartTime and aEndTime
       
   206  */
       
   207 inline TTimeIntervalMicroSeconds32 Elapsed(
       
   208     const TTime& aStartTime,
       
   209     const TTime& aEndTime )
       
   210     {
       
   211     return aEndTime.MicroSecondsFrom( aStartTime ).Int64();
       
   212     }
       
   213 
       
   214 // ----------------------------------------------------------------------------
       
   215 // SetComplete
       
   216 // ----------------------------------------------------------------------------
       
   217 //
       
   218 void CXnGesture::SetComplete()
       
   219     {
       
   220     __ASSERT_DEBUG( iPoints.Count() > 0, Panic( EGesturePanicIllegalLogic ) );
       
   221     iState = EComplete;
       
   222     iCompletionTime = CurrentTime();
       
   223     }
       
   224 
       
   225 // ----------------------------------------------------------------------------
       
   226 // SetComplete
       
   227 // ----------------------------------------------------------------------------
       
   228 //
       
   229 void CXnGesture::SetCancelled()
       
   230     {
       
   231     iState = ECancelled;
       
   232     }
       
   233 
       
   234 // ----------------------------------------------------------------------------
       
   235 // IsTap
       
   236 // ----------------------------------------------------------------------------
       
   237 //
       
   238 TBool CXnGesture::IsTap() const
       
   239     {
       
   240     return CodeFromPoints( EAxisBoth ) == EGestureTap;
       
   241     }
       
   242 
       
   243 /**
       
   244  * Translates a non-holding code into a holding code
       
   245  * @param aCode original gesture code
       
   246  * @return a gesture code with hold flag applied
       
   247  */
       
   248 inline TXnGestureCode Hold( TXnGestureCode aCode )
       
   249     {
       
   250     if ( aCode != EGestureStart &&
       
   251          aCode != EGestureDrag &&
       
   252          aCode != EGestureReleased &&
       
   253          aCode != EGestureUnknown )
       
   254         {
       
   255         return static_cast< TXnGestureCode >( aCode | EFlagHold );
       
   256         }
       
   257     return aCode;
       
   258     }
       
   259 
       
   260 // ----------------------------------------------------------------------------
       
   261 // Code
       
   262 // ----------------------------------------------------------------------------
       
   263 //
       
   264 TXnGestureCode CXnGesture::Code( TAxis aRelevantAxis ) const
       
   265     {
       
   266     switch ( iState )
       
   267         {
       
   268         case ENotComplete:
       
   269             // "start" event if only first point received
       
   270             // need to check that not holding, in case user pressed stylus
       
   271             // down, and activated holding without moving the stylus
       
   272             if ( iPoints.Count() == 1 && !IsHolding() )
       
   273                 {
       
   274                 return EGestureStart;
       
   275                 }
       
   276             // "drag" event if holding not started or holding started earlier
       
   277             else if ( iHoldingState != EHoldStarting )
       
   278                 {
       
   279                 return EGestureDrag;
       
   280                 }
       
   281             // holding was just started
       
   282             else
       
   283                 {
       
   284                 return Hold( CodeFromPoints( aRelevantAxis ) );
       
   285                 }
       
   286 
       
   287         case EComplete:
       
   288             {
       
   289             TXnGestureCode code = CodeFromPoints( aRelevantAxis );
       
   290 
       
   291 #ifdef _GESTURE_DOUBLE_TAP_SUPPORT
       
   292             if ( EGestureTap == code && IsTapDoubleTap() )
       
   293                 {
       
   294                 code = EGestureDoubleTap;
       
   295                 }
       
   296 #endif // _GESTURE_DOUBLE_TAP_SUPPORT
       
   297 
       
   298             return code;
       
   299             }
       
   300 
       
   301         case EReleased:
       
   302             return EGestureReleased;
       
   303 
       
   304         case ECancelled: // fallthrough
       
   305         default:
       
   306             return EGestureUnknown;
       
   307         }
       
   308     }
       
   309 
       
   310 // ----------------------------------------------------------------------------
       
   311 // IsHolding
       
   312 // ----------------------------------------------------------------------------
       
   313 //
       
   314 TBool CXnGesture::IsHolding() const
       
   315     {
       
   316     return iHoldingState >= EHoldStarting;
       
   317     }
       
   318 
       
   319 // ----------------------------------------------------------------------------
       
   320 // StartPos
       
   321 // ----------------------------------------------------------------------------
       
   322 //
       
   323 TPoint CXnGesture::StartPos() const
       
   324     {
       
   325     // at least one point will be in the array during callback (pointer down pos)
       
   326     return iPoints[0].iPos;
       
   327     }
       
   328 
       
   329 // ----------------------------------------------------------------------------
       
   330 // CurrentPos
       
   331 // ----------------------------------------------------------------------------
       
   332 //
       
   333 TPoint CXnGesture::CurrentPos() const
       
   334     {
       
   335     // at least on point will be in the array during callback (pointer down pos)
       
   336     return iPoints[iPoints.Count() - 1].iPos;
       
   337     }
       
   338 
       
   339 // ----------------------------------------------------------------------------
       
   340 // IsMovementStopped
       
   341 // ----------------------------------------------------------------------------
       
   342 //
       
   343 inline TBool CXnGesture::IsMovementStopped() const
       
   344     {
       
   345     // iCompletionTime is only only valid if client has called SetComplete
       
   346     if ( iState >= EComplete )
       
   347         {
       
   348         return Elapsed( NthLastEntry( 1 ).iTime, iCompletionTime )
       
   349             .Int() > KSpeedStopTime;
       
   350         }
       
   351     return EFalse;
       
   352     }
       
   353 
       
   354 namespace
       
   355     {
       
   356     const TInt KFloatingPointAccuracy = 0.000001;
       
   357 
       
   358     /** @return percentage (0.0-1.0) how far aPos is from aEdge1 towards aEdge2 */
       
   359     inline TReal32 Proportion( TReal32 aPos, TReal32 aEdge1, TReal32 aEdge2 )
       
   360         {
       
   361         if ( Abs( aEdge2 - aEdge1 ) > KFloatingPointAccuracy )
       
   362             {
       
   363             return ( aPos - aEdge1 ) / ( aEdge2 - aEdge1 );
       
   364             }
       
   365         return 0; // avoid division by zero
       
   366         }
       
   367 
       
   368     /** Edges (pixels) at which speed should be -100% or 100% */
       
   369     NONSHARABLE_STRUCT( TEdges )
       
   370         {
       
   371         TReal32 iMin;
       
   372         TReal32 iMax;
       
   373         };
       
   374 
       
   375     /**
       
   376      * scale which allows different (coordinate -> percentage) mapping
       
   377      * between -100% to 0% and 0 and 100%
       
   378      */
       
   379     NONSHARABLE_STRUCT( TScale )
       
   380         {
       
   381         TScale( TInt aZero, const TEdges& aEdges )
       
   382                 : iMin( aEdges.iMin ), iZero( aZero ), iMax( aEdges.iMax )
       
   383             {
       
   384             }
       
   385 
       
   386         /** @return aPos as a percentage between -100% and 100% in aScale */
       
   387         TReal32 Percent( TReal32 aPos ) const;
       
   388 
       
   389         /// coordinate where speed is -100%
       
   390         TReal32 iMin;
       
   391         /// coordinate where speed is 0%
       
   392         TReal32 iZero;
       
   393         /// coordinate where speed is 100%
       
   394         TReal32 iMax;
       
   395         };
       
   396 
       
   397     /** @convert aPos into a percentage between -100% and 100% in aScale */
       
   398     TReal32 TScale::Percent( TReal32 aPos ) const
       
   399         {
       
   400         TReal32 percent;
       
   401         if ( aPos < iZero )
       
   402             {
       
   403             // return negative percentages on the lower side of zero point
       
   404             percent = -1 * Proportion( aPos, iZero, iMin );
       
   405             }
       
   406         else
       
   407             {
       
   408             percent = Proportion( aPos, iZero, iMax );
       
   409             }
       
   410         // constrain between -100% and 100%
       
   411         return Min( Max( percent, -1.0F ), 1.0F );
       
   412         }
       
   413 
       
   414     /** Scale in x and y dimensions */
       
   415     NONSHARABLE_STRUCT( TScale2D )
       
   416         {
       
   417         TRealPoint Percent( const TPoint& aPos ) const
       
   418             {
       
   419             return TRealPoint(
       
   420                 iX.Percent( aPos.iX ),
       
   421                 iY.Percent( aPos.iY ) );
       
   422             }
       
   423 
       
   424         TScale iX;
       
   425         TScale iY;
       
   426         };
       
   427 
       
   428     enum TDirection { ESmaller, ELarger };
       
   429 
       
   430     /** @return the direction of pos compared to the previous pos */
       
   431     inline TDirection Direction( TInt aPos, TInt aPreviousPos )
       
   432         {
       
   433         return aPos < aPreviousPos ? ESmaller : ELarger;
       
   434         }
       
   435 
       
   436     /** Direction in x and y dimensions */
       
   437     NONSHARABLE_STRUCT( TDirection2D )
       
   438         {
       
   439         TDirection iX;
       
   440         TDirection iY;
       
   441         };
       
   442 
       
   443     /** Return the direction (up/down) of signal at aIndex */
       
   444     inline TDirection2D Direction(
       
   445         TInt aIndex,
       
   446         const RArray< TXnPointEntry >& aPoints )
       
   447         {
       
   448         const TPoint& pos = aPoints[aIndex].iPos;
       
   449         const TPoint& prevPos = aPoints[aIndex - 1].iPos;
       
   450         TDirection2D dir = {
       
   451             Direction( pos.iX, prevPos.iX ),
       
   452             Direction( pos.iY, prevPos.iY ) };
       
   453         return dir;
       
   454         }
       
   455 
       
   456     /**
       
   457      * @return a position in the aLow and aHigh, so that it aProportion of
       
   458      *         of length is above the pos
       
   459      */
       
   460     TReal32 ProportionalLength( TReal32 aLow, TReal32 aHigh, TReal32 aProportion )
       
   461         {
       
   462         return ( aHigh - aLow ) * aProportion / ( 1 + aProportion );
       
   463         }
       
   464 
       
   465     /**
       
   466      * @return aVariableEdge scaled to new position, when the other edge changes
       
   467      *         from aOldEdge to aNewEdge, so that aOrigin maintains the *same
       
   468      *         relative position* between aVariableEdge and the other edge
       
   469      */
       
   470     inline TReal32 ScaledEdge(
       
   471         TReal32 aOrigin,
       
   472         TReal32 aVariableEdge,
       
   473         TReal32 aOldEdge,
       
   474         TReal aNewEdge )
       
   475         {
       
   476         TReal32 proportion = Proportion( aOrigin, aVariableEdge, aOldEdge );
       
   477         return ( proportion * aNewEdge - aOrigin ) / ( proportion - 1 );
       
   478         }
       
   479 
       
   480     TScale Rescale(
       
   481         TReal32 aPos,
       
   482         TDirection aDir,
       
   483         TDirection aPrevDir,
       
   484         const TScale& aPrevScale,
       
   485         const TEdges& aEdges )
       
   486         {
       
   487         TScale scale( aPrevScale );
       
   488         if ( aPrevDir != aDir )
       
   489             {
       
   490             // the code duplication is accepted here, since it is difficult
       
   491             // to factor out while maintaining the understandability of this
       
   492             // anyway complex algorithm
       
   493             if ( aDir == ESmaller )
       
   494                 {
       
   495                 scale.iMin = aEdges.iMin;
       
   496                 if ( aPrevScale.iZero < aPos )
       
   497                     {
       
   498                     TReal32 proportionAboveZero = Proportion(
       
   499                         aPos, aPrevScale.iZero, aPrevScale.iMax );
       
   500                     scale.iZero = aPos - ProportionalLength(
       
   501                         aEdges.iMin, aPos, proportionAboveZero );
       
   502                     }
       
   503                 else
       
   504                     {
       
   505                     // adjust zero pos so that proportion between aPos, Min,
       
   506                     // and Zero pos stay the same (Min will move to 0,
       
   507                     // aPos stays the same)
       
   508                     scale.iZero = ScaledEdge( aPos, aPrevScale.iZero,
       
   509                         aPrevScale.iMin, aEdges.iMin );
       
   510                     }
       
   511 
       
   512                 // adjust the upper edge to take into account the movement of
       
   513                 // zero pos
       
   514                 scale.iMax = ScaledEdge( aPos, aPrevScale.iMax,
       
   515                     aPrevScale.iZero, scale.iZero );
       
   516                 }
       
   517             else // ELarger
       
   518                 {
       
   519                 scale.iMax = aEdges.iMax;
       
   520                 if ( aPos < aPrevScale.iZero )
       
   521                     {
       
   522                     TReal32 proportionBelowZero = Proportion(
       
   523                         aPos, aPrevScale.iZero, aPrevScale.iMin );
       
   524                     scale.iZero = aPos + ProportionalLength(
       
   525                         aPos, aEdges.iMax, proportionBelowZero );
       
   526                     }
       
   527                 else
       
   528                     {
       
   529                     // adjust zero pos so that proportion between aPos, Max, and
       
   530                     // Zero pos stay the same (Max will move edge, aPos stays
       
   531                     // the same)
       
   532                     scale.iZero = ScaledEdge( aPos, aPrevScale.iZero,
       
   533                         aPrevScale.iMax, aEdges.iMax );
       
   534                     }
       
   535 
       
   536                 // adjust the lower edge to take into account the movement of
       
   537                 // zero pos
       
   538                 scale.iMin = ScaledEdge( aPos, aPrevScale.iMin,
       
   539                     aPrevScale.iZero, scale.iZero );
       
   540                 }
       
   541             }
       
   542         return scale;
       
   543         }
       
   544 
       
   545     /** Edges in x and y dimensions */
       
   546     NONSHARABLE_STRUCT( TEdges2D )
       
   547         {
       
   548         TEdges iX;
       
   549         TEdges iY;
       
   550         };
       
   551 
       
   552     /**
       
   553      * @param aEdges edges of the area in which gesture points are accepted
       
   554      * @return the scale of latest point in the list of points
       
   555      */
       
   556     TScale2D Scale( const RArray< TXnPointEntry >& aPoints, const TEdges2D& aEdges )
       
   557         {
       
   558         TScale2D scale = { TScale( aPoints[0].iPos.iX, aEdges.iX ),
       
   559                            TScale( aPoints[0].iPos.iY, aEdges.iY ) };
       
   560         TInt count = aPoints.Count();
       
   561         if ( count > 1 )
       
   562             {
       
   563             // iterate the whole point list to arrive to the current scale
       
   564             TDirection2D dir( Direction( 1, aPoints ) );
       
   565             for ( TInt i = 1; i < count; i++ )
       
   566                 {
       
   567                 // get direction at i
       
   568                 TDirection2D newDir( Direction( i, aPoints ) );
       
   569                 // get new scale at i
       
   570                 scale.iX = Rescale(
       
   571                     aPoints[i - 1].iPos.iX,
       
   572                     newDir.iX,
       
   573                     dir.iX,
       
   574                     scale.iX,
       
   575                     aEdges.iX );
       
   576                 scale.iY = Rescale(
       
   577                     aPoints[i - 1].iPos.iY,
       
   578                     newDir.iY,
       
   579                     dir.iY,
       
   580                     scale.iY,
       
   581                     aEdges.iY );
       
   582                 dir = newDir;
       
   583                 }
       
   584             }
       
   585         return scale;
       
   586         }
       
   587     } // unnamed namespace
       
   588 
       
   589 TRealPoint CXnGesture::SpeedPercent( const TRect& aEdges ) const
       
   590     {
       
   591     // x and y coordinates are easier to handle separately, extract from TRect:
       
   592     // ((iMinX, iMinY), (iMaxX, iMaxY)) -> ((iMinX, iMaxX), (iMinY, iMaxY))
       
   593     TEdges2D edges = {
       
   594         { aEdges.iTl.iX, aEdges.iBr.iX },
       
   595         { aEdges.iTl.iY, aEdges.iBr.iY } };
       
   596     // work out the current scale (coordinate -> percentage mapping) from
       
   597     // the history of points (i.e., points of current gesture). Then
       
   598     // calculate the percentage of the current position.
       
   599     return Scale( iPoints, edges ).Percent( CurrentPos() );
       
   600     }
       
   601 
       
   602 // ----------------------------------------------------------------------------
       
   603 // Speed
       
   604 // ----------------------------------------------------------------------------
       
   605 //
       
   606 TRealPoint CXnGesture::Speed() const
       
   607     {
       
   608     const TReal32 KMicroSecondsInSecond = 1000000;
       
   609 
       
   610     // Speed is only evaluated at the end of the swipe
       
   611     // if user stops at the end of the swipe before lifting stylus,
       
   612     // speed is zero. If time is zero, return 0 speed (infinite does
       
   613     // not make sense either). Will need to consider also earlier points
       
   614     // and their times or start time, if this zero-speed behavior is a problem
       
   615     TRealPoint speed;
       
   616     TReal32 time = static_cast< TReal32 >( TimeFromPreviousPoint().Int() )
       
   617         / KMicroSecondsInSecond;
       
   618     if ( !IsMovementStopped() && time > 0 )
       
   619         {
       
   620         TPoint distance = CurrentPos() - PreviousPos();
       
   621         speed.iX = static_cast< TReal32 >( distance.iX ) / time;
       
   622         speed.iY = static_cast< TReal32 >( distance.iY ) / time;
       
   623         }
       
   624     return speed;
       
   625     }
       
   626 
       
   627 // ----------------------------------------------------------------------------
       
   628 // Distance
       
   629 // ----------------------------------------------------------------------------
       
   630 //
       
   631 TPoint CXnGesture::Distance() const
       
   632     {
       
   633     return CurrentPos() - StartPos();
       
   634     }
       
   635 
       
   636 // ----------------------------------------------------------------------------
       
   637 // TimeFromPreviousPoint
       
   638 // ----------------------------------------------------------------------------
       
   639 //
       
   640 inline TTimeIntervalMicroSeconds32 CXnGesture::TimeFromPreviousPoint() const
       
   641     {
       
   642     const TInt KLatestEntryOffset = 1;
       
   643     return Elapsed( PreviousEntry().iTime, NthLastEntry( KLatestEntryOffset ).iTime );
       
   644     }
       
   645 
       
   646 // ----------------------------------------------------------------------------
       
   647 // CodeFromPoints
       
   648 // ----------------------------------------------------------------------------
       
   649 //
       
   650 TXnGestureCode CXnGesture::CodeFromPoints( TAxis aRelevantAxis ) const
       
   651     {
       
   652     // select the correct filter based on aRelevantAxis
       
   653     // these filter_ objects are array decorators that will eliminate either
       
   654     // x, y or neither coordinate of each point
       
   655     TXAxisPointArray filterY( iPoints );
       
   656     TYAxisPointArray filterX( iPoints );
       
   657     TXnPointArray filterNone( iPoints );
       
   658     TXnPointArray& filter =
       
   659         aRelevantAxis == EAxisHorizontal ? static_cast< TXnPointArray& >( filterY ) :
       
   660         aRelevantAxis == EAxisVertical   ? static_cast< TXnPointArray& >( filterX ) :
       
   661         /* otherwise EAxisBoth */          filterNone;
       
   662 
       
   663     // currently the gesture recogniser does not have any state, so it is fast
       
   664     // to instantiate. The call is not static however, to allow the recogniser
       
   665     // to be replaced by a more complicated implementation that has state.
       
   666     // then it may make sense to make the recogniser a member variable.
       
   667     return TXnGestureRecogniser().GestureCode( filter );
       
   668     }
       
   669 
       
   670 // ----------------------------------------------------------------------------
       
   671 // CodeFromPoints
       
   672 // ----------------------------------------------------------------------------
       
   673 //
       
   674 TXnGestureCode CXnGesture::LastDirection( TAxis aRelevantAxis ) const
       
   675     {
       
   676     // select the correct filter based on aRelevantAxis
       
   677     // these filter_ objects are array decorators that will eliminate either
       
   678     // x, y or neither coordinate of each point
       
   679     TXAxisPointArray filterY( iPoints );
       
   680     TYAxisPointArray filterX( iPoints );
       
   681     TXnPointArray filterNone( iPoints );
       
   682     TXnPointArray& filter =
       
   683         aRelevantAxis == EAxisHorizontal ? static_cast< TXnPointArray& >( filterY ) :
       
   684         aRelevantAxis == EAxisVertical   ? static_cast< TXnPointArray& >( filterX ) :
       
   685         /* otherwise EAxisBoth */          filterNone;
       
   686 
       
   687     // currently the gesture recogniser does not have any state, so it is fast
       
   688     // to instantiate. The call is not static however, to allow the recogniser
       
   689     // to be replaced by a more complicated implementation that has state.
       
   690     // then it may make sense to make the recogniser a member variable.
       
   691     return TXnGestureRecogniser().LastDirection( filter );
       
   692     }
       
   693 
       
   694 // ----------------------------------------------------------------------------
       
   695 // return nth point from the end of the points array
       
   696 // ----------------------------------------------------------------------------
       
   697 //
       
   698 inline const TXnPointEntry& CXnGesture::NthLastEntry( TInt aOffset ) const
       
   699     {
       
   700     return iPoints[Max( iPoints.Count() - aOffset, 0 )];
       
   701     }
       
   702 
       
   703 // ----------------------------------------------------------------------------
       
   704 // PreviousEntry
       
   705 // ----------------------------------------------------------------------------
       
   706 //
       
   707 inline const TXnPointEntry& CXnGesture::PreviousEntry() const
       
   708     {
       
   709     return NthLastEntry( KPreviousPointOffset );
       
   710     }
       
   711 
       
   712 // ----------------------------------------------------------------------------
       
   713 // PreviousPos
       
   714 // ----------------------------------------------------------------------------
       
   715 //
       
   716 inline TPoint CXnGesture::PreviousPos() const
       
   717     {
       
   718     return NthLastEntry( KPreviousPointOffset - 1 ).iPos;
       
   719     }
       
   720 
       
   721 // ----------------------------------------------------------------------------
       
   722 // SetComplete
       
   723 // ----------------------------------------------------------------------------
       
   724 //
       
   725 TBool CXnGesture::IsTapDoubleTap() const
       
   726     {
       
   727     return iPreviousGesture.iType == TGestureRecord::ETypeTap &&
       
   728            Elapsed( iPreviousGesture.iCompletionTime, iCompletionTime ).Int() <=
       
   729                KMaxDoubleTapDuration &&
       
   730            IsNear( iPreviousGesture.iPos, iPoints[iPoints.Count() - 1].iPos,
       
   731                KSamePointTolerance );
       
   732     }
       
   733 
       
   734 // ----------------------------------------------------------------------------
       
   735 // Type
       
   736 // ----------------------------------------------------------------------------
       
   737 //
       
   738 CXnGesture::TGestureRecord::TType CXnGesture::Type() const
       
   739     {
       
   740     if ( CodeFromPoints( EAxisBoth ) == EGestureTap && !IsHolding() )
       
   741         {
       
   742         if ( IsTapDoubleTap() )
       
   743             {
       
   744             return CXnGesture::TGestureRecord::ETypeDoubleTap;
       
   745             }
       
   746         else
       
   747             {
       
   748             return CXnGesture::TGestureRecord::ETypeTap;
       
   749             }
       
   750         }
       
   751     else
       
   752         {
       
   753         return CXnGesture::TGestureRecord::ETypeOther;
       
   754         }
       
   755     }
       
   756 
       
   757 // ----------------------------------------------------------------------------
       
   758 // TGestureRecord constructor
       
   759 // ----------------------------------------------------------------------------
       
   760 //
       
   761 CXnGesture::TGestureRecord::TGestureRecord()
       
   762     {
       
   763     iType = ETypeOther;
       
   764     }
       
   765 
       
   766 // ----------------------------------------------------------------------------
       
   767 // TGestureRecord constructor
       
   768 // ----------------------------------------------------------------------------
       
   769 //
       
   770 CXnGesture::TGestureRecord::TGestureRecord(
       
   771     CXnGesture::TGestureRecord::TType aType,
       
   772     TTime aCompletionTime,
       
   773     TPoint aPos )
       
   774         : iType( aType ), iCompletionTime( aCompletionTime ), iPos( aPos )
       
   775     {
       
   776     }