AppSrc/Gesture.cpp
changeset 3 93fff7023be8
equal deleted inserted replaced
2:e1e28b0273b0 3:93fff7023be8
       
     1 /*
       
     2 * Copyright (c) 2009 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: Juha Kauppinen, Mika Hokkanen
       
    13 * 
       
    14 * Description: Photo Browser
       
    15 *
       
    16 */
       
    17 
       
    18 #include "debug.h"
       
    19 #include "Gesture.h"
       
    20 #include <aknnotewrappers.h>
       
    21 
       
    22 // TODO: Add flexibility to support more gesture. e.g. circle
       
    23 // TODO: Add tap range (size of allowed moving area for tap)
       
    24 // TODO: Add mode-type working. Change config by one call
       
    25 
       
    26 CGesture::CGesture(MGestureCallBack* aOwner)
       
    27     //: CTimer(CActive::EPriorityUserInput)
       
    28 : CTimer(CActive::EPriorityHigh)
       
    29     , iState(EWaiting)
       
    30     , iOwner(aOwner)
       
    31     , iFirstGesture(EGestureNone)
       
    32     , iThresholdOfTap(KDefaultThresholdOfTapPixels)
       
    33     , iThresholdOfCursor(KDefaultThresholdOfCursorPixels)
       
    34     , iStationaryTime(KDefaultStationaryTime)
       
    35     , iLongTapTime(KDefaultLongTapTime)
       
    36     , iMonitoringTime(KDefaultMonitoringTime)
       
    37     , iSafetyTime(KDefaultSafetyTime)
       
    38     {
       
    39     // No implementation required
       
    40     }
       
    41 
       
    42 CGesture::~CGesture()
       
    43     {
       
    44     Cancel(); // CTimer
       
    45     iDragPoints.Close();
       
    46     iDragTicks.Close();
       
    47     }
       
    48 
       
    49 CGesture* CGesture::NewLC(MGestureCallBack* aOwner)
       
    50     {
       
    51     CGesture* self = new (ELeave) CGesture(aOwner);
       
    52     CleanupStack::PushL(self);
       
    53     self->ConstructL();
       
    54     return self;
       
    55     }
       
    56 
       
    57 CGesture* CGesture::NewL(MGestureCallBack* aOwner)
       
    58     {
       
    59     CGesture* self = CGesture::NewLC(aOwner);
       
    60     CleanupStack::Pop(); // self;
       
    61     return self;
       
    62     }
       
    63 
       
    64 void CGesture::ConstructL()
       
    65     {
       
    66     CTimer::ConstructL();
       
    67     CActiveScheduler::Add(this);
       
    68     }
       
    69 
       
    70 EXPORT_C void CGesture::PointerEventL( const TPointerEvent& aEvent )
       
    71     {
       
    72 #define RESETPOINTS()        {iDragPoints.Reset(); iDragTicks.Reset();}
       
    73 #define RECORDPOINTS(p, t)   {iDragPoints.Append(p); iDragTicks.Append(t);}
       
    74 #define SETMOVEMENT(d, c, p) {d.iX=c.iX-p.iX; d.iY=c.iY-p.iY;}
       
    75 
       
    76     TInt tick = User::NTickCount();
       
    77     TGestureType type = EGestureNone;
       
    78     TPoint delta;
       
    79     TPoint vector;
       
    80     TInt threshold;
       
    81 
       
    82     switch( aEvent.iType )
       
    83         {
       
    84         case TPointerEvent::EButton1Down:
       
    85             DP2_IMAGIC(_L("CGesture::PointerEventL: TPointerEvent::EButton1Down (%d, %d)"), aEvent.iPosition.iX, aEvent.iPosition.iY);
       
    86             
       
    87             if (iState == EEnded) break; // do nothing if it's in safety time
       
    88             
       
    89             // Start timer to monitor long tap
       
    90             Cancel(); // CTimer. Stop all timers.
       
    91             DP1_IMAGIC(_L("iStationaryTime=%d"), iStationaryTime);
       
    92             After( TTimeIntervalMicroSeconds32(iStationaryTime));
       
    93 
       
    94             // Initialise pointer records and start new record
       
    95             iPointBegan = iPointLastCursor = iPointPreviousEvent = aEvent.iPosition;
       
    96             RESETPOINTS();
       
    97             RECORDPOINTS(aEvent.iPosition, tick);
       
    98 
       
    99             if (iState == EMonitoring)
       
   100                 {
       
   101                 DP0_IMAGIC(_L("CGesture::PointerEventL (2nd gesture starting)"));
       
   102                 // store 1st gesture type in high 16 bits if this is 2nd gesture
       
   103                 iFirstGesture = iFirstGesture << 16;
       
   104                 }
       
   105             else // iState should be Waiting. Resets anyway even if it's not. 
       
   106                 {
       
   107                 DP0_IMAGIC(_L("CGesture::PointerEventL (1st gesture starting)"));
       
   108                 // Set point only for 1st gesture and notify owner
       
   109                 iFirstGesture    = EGestureNone;
       
   110                 iPointFirstBegan = aEvent.iPosition;
       
   111                 iOwner->HandleGestureBeganL(aEvent.iPosition);
       
   112                 }
       
   113 
       
   114             iState = EBegan;
       
   115 
       
   116             DP1_IMAGIC(_L("####### Down:iFirstGesture=%x"), iFirstGesture);
       
   117             break;
       
   118 
       
   119         case TPointerEvent::EDrag:
       
   120             DP2_IMAGIC(_L("CGesture::PointerEventL: TPointerEvent::EDrag (%d, %d)"), aEvent.iPosition.iX, aEvent.iPosition.iY);
       
   121 
       
   122             if ((iState != EBegan) && (iState != EStationary) && (iState != ETravelling))
       
   123                 break; // do nothing if it's not in the state above
       
   124             
       
   125             // record drag points and thier tick counts
       
   126             RECORDPOINTS(aEvent.iPosition, tick);
       
   127 
       
   128             SETMOVEMENT(delta, aEvent.iPosition, iPointPreviousEvent);
       
   129 
       
   130             threshold = (iState == EBegan)? iThresholdOfTap * 3: iThresholdOfTap;
       
   131             
       
   132             if (IsMovementWithinThreshold(delta, threshold))
       
   133                 {
       
   134                 DP0_IMAGIC(_L("CGesture::PointerEventL (staying within threshold)"));
       
   135                 }
       
   136             else
       
   137                 {
       
   138                 DP0_IMAGIC(_L("CGesture::PointerEventL (going beyond threshold)"));
       
   139                 Cancel(); // CTimer. Stop all timers.
       
   140                 iState = ETravelling;
       
   141                 }
       
   142 
       
   143             if (iState == ETravelling)
       
   144                 {
       
   145                 DP0_IMAGIC(_L("CGesture::PointerEventL (notify drag event)"));
       
   146                 iOwner->HandleGestureMovedL(delta, EGestureDrag);
       
   147                 iPointPreviousEvent = aEvent.iPosition;
       
   148                 }
       
   149 
       
   150 #ifdef CURSOR_SIMULATION
       
   151             type = CheckMovement(iPointLastCursor, aEvent.iPosition);
       
   152 
       
   153             if (type != EGestureNone)
       
   154                 {
       
   155                 iOwner->HandleGestureMovedL(aEvent.iPosition, EGestureCursor|type);
       
   156                 iPointLastCursor = aEvent.iPosition;
       
   157                 }
       
   158             DP5_IMAGIC(_L("iPointLastCursor(%d)"), type);
       
   159 #endif
       
   160             break;
       
   161 
       
   162         case TPointerEvent::EButton1Up:
       
   163             DP2_IMAGIC(_L("CGesture::PointerEventL: TPointerEvent::EButton1Up (%d, %d)"), aEvent.iPosition.iX, aEvent.iPosition.iY);
       
   164 
       
   165             if ((iState != EBegan) && (iState != EStationary) && (iState != ETravelling))
       
   166                 break; // do nothing if it's not in the state above
       
   167             
       
   168             Cancel(); // Stop timers
       
   169             
       
   170             iPointPreviousEvent = aEvent.iPosition;
       
   171 
       
   172             // record drag points and thier tick counts
       
   173             RECORDPOINTS(aEvent.iPosition, tick);
       
   174 
       
   175             if ((iState == EBegan) ||
       
   176                 ((iState == EStationary) && !IS_GESTURE_LONGTAPPING(iFirstGesture)))
       
   177                 {
       
   178                 DP0_IMAGIC(_L("CGesture::PointerEventL (Tap!)"));
       
   179                 type = EGestureTap;
       
   180                 
       
   181                 // TODO: check distance of each tap if it's double tap
       
   182                 iFirstGesture |= type;
       
   183 
       
   184                 TInt t = (IS_GESTURE_TAPPED(iFirstGesture))? 0: iMonitoringTime;
       
   185                 After(TTimeIntervalMicroSeconds32(t)); // call immediately if double tap
       
   186                 iState = EMonitoring;
       
   187                 }
       
   188             else if ((iState == EStationary) && IS_GESTURE_LONGTAPPING(iFirstGesture))
       
   189                 {
       
   190                 DP0_IMAGIC(_L("CGesture::PointerEventL (Long tap!)"));
       
   191                 type = EGestureLongTap;
       
   192 
       
   193                 iFirstGesture |= type;
       
   194                 After(TTimeIntervalMicroSeconds32(iMonitoringTime));
       
   195                 iState = EMonitoring;
       
   196                 }
       
   197             else if (iState == ETravelling)
       
   198                 {
       
   199                 DP0_IMAGIC(_L("CGesture::PointerEventL (Was drag. Flick!)"));
       
   200                 type = CheckFlick(KDefaultValidityTimeOfFlick, vector);
       
   201                 iPointPreviousEvent = vector;
       
   202 
       
   203                 iFirstGesture |= type;
       
   204                 After(TTimeIntervalMicroSeconds32(0)); // call immediately 
       
   205                 iState = EMonitoring;
       
   206                 }
       
   207 
       
   208             DP1_IMAGIC(_L("####### Up:iFirstGesture=%x"), iFirstGesture);
       
   209             break;
       
   210 
       
   211         default:
       
   212             break;
       
   213         }
       
   214     }
       
   215 
       
   216 EXPORT_C void CGesture::SetThresholdOfTap( const TInt aPixels)
       
   217     {
       
   218     iThresholdOfTap = aPixels;
       
   219     }
       
   220 
       
   221 EXPORT_C void CGesture::SetThresholdOfCursor( const TInt aPixels)
       
   222     {
       
   223     iThresholdOfCursor = aPixels;
       
   224     }
       
   225 
       
   226 EXPORT_C void CGesture::SetStationaryTime(const TInt aMicroseconds)
       
   227     {
       
   228     iStationaryTime = aMicroseconds;
       
   229     }
       
   230 
       
   231 EXPORT_C void CGesture::SetLongTapTime(const TInt aMicroSeconds)
       
   232     {
       
   233     iLongTapTime = aMicroSeconds;
       
   234     }
       
   235 
       
   236 EXPORT_C void CGesture::SetMonitoringTime(const TInt aMicroseconds)
       
   237     {
       
   238     iMonitoringTime = aMicroseconds;
       
   239     }
       
   240 
       
   241 EXPORT_C void CGesture::SetSafetyTime(const TInt aMicroseconds)
       
   242     {
       
   243     iSafetyTime = aMicroseconds;
       
   244     }
       
   245 
       
   246 TBool CGesture::IsMovementWithinThreshold(const TPoint aDelta, const TInt aThreshold)
       
   247     {
       
   248     TBool ret = ETrue;
       
   249 
       
   250     TInt diff_x      = aDelta.iX;
       
   251     TInt diff_y      = aDelta.iY;
       
   252     TInt abs_diff_2  = diff_x * diff_x + diff_y * diff_y;
       
   253     TInt threshold_2 = aThreshold * aThreshold;
       
   254 
       
   255     if (abs_diff_2 > threshold_2) ret = EFalse;
       
   256     
       
   257     return ret;
       
   258     }
       
   259 
       
   260 TGestureType CGesture::CheckMovement(const TPoint aPointPrevious, const TPoint aPointCurrent, const TBool aSkipThresholdCheck)
       
   261     {
       
   262     TGestureType ret = EGestureNone;
       
   263 
       
   264     TInt diff_x  = aPointCurrent.iX - aPointPrevious.iX;
       
   265     TInt diff_y  = aPointCurrent.iY - aPointPrevious.iY;
       
   266 
       
   267     TInt abs_diff_x = Abs(diff_x);
       
   268     TInt abs_diff_y = Abs(diff_y);
       
   269     TInt abs_diff_2 = abs_diff_x*abs_diff_x + abs_diff_y*abs_diff_y;  
       
   270 
       
   271     if (abs_diff_2 > iThresholdOfCursor*iThresholdOfCursor)
       
   272         {
       
   273         // Movement is mapped to one of 8 directions
       
   274         TBool valid_x = (abs_diff_x && (abs_diff_x * 5 > abs_diff_y * 4))? ETrue: EFalse;
       
   275         TBool valid_y = (abs_diff_y && (abs_diff_y * 5 > abs_diff_x * 4))? ETrue: EFalse;
       
   276 
       
   277         if (valid_y)
       
   278             {
       
   279             if (diff_y < 0) ret |= EGestureUp;
       
   280             else            ret |= EGestureDown;
       
   281             }
       
   282         if (valid_x)
       
   283             {
       
   284             if (diff_x < 0) ret |= EGestureLeft;
       
   285             else            ret |= EGestureRight;
       
   286             }
       
   287         }
       
   288     
       
   289     return ret;
       
   290     }
       
   291 
       
   292 TGestureType CGesture::CheckFlick(const TInt aValidityTime, TPoint& aVector)
       
   293     {
       
   294     DP0_IMAGIC(_L("CGesture::CheckFlick++"));
       
   295 
       
   296     // TODO: need to check if counts are same in iDragPoints and iDragTicks
       
   297     TInt first, last;
       
   298     TInt validtick = aValidityTime / 1000; // convert micro sec to milli sec.
       
   299     TGestureType ret = EGestureNone;
       
   300 
       
   301     first = last = iDragPoints.Count() - 1;
       
   302     aVector.iX = aVector.iY = 0;
       
   303     
       
   304     for (TInt i=last-1;i>=0;--i)
       
   305         {
       
   306         TInt tickdiff = iDragTicks[last] - iDragTicks[i];
       
   307 
       
   308         DP5_IMAGIC(_L("i=%d, tick=%d, tickdiff=%d (x,y)=(%d,%d)"),
       
   309                 i, iDragTicks[i], tickdiff, iDragPoints[i].iX, iDragPoints[i].iY);
       
   310 
       
   311         if (tickdiff < validtick)
       
   312             {
       
   313             first = i;
       
   314             }
       
   315         else
       
   316             {
       
   317             break;
       
   318             }
       
   319         }
       
   320 
       
   321     if (first != last)
       
   322         {
       
   323         TInt tickdiff = iDragTicks[last] - iDragTicks[first];
       
   324         ret = CheckMovement(iDragPoints[first], iDragPoints[last], ETrue);
       
   325 
       
   326         // Tick diff is 100 at minimum to avoid returning extreamly big vector.
       
   327         if (tickdiff < 100) tickdiff = 100;
       
   328 
       
   329         // Calculate the movement speed = pixels/sec
       
   330         aVector.iX = (iDragPoints[last].iX - iDragPoints[first].iX)*1000/tickdiff;
       
   331         aVector.iY = (iDragPoints[last].iY - iDragPoints[first].iY)*1000/tickdiff;
       
   332         }
       
   333 
       
   334     DP5_IMAGIC(_L("%d~%d, %d=>%d =%d"),
       
   335             first, last, iDragPoints[first].iX, iDragPoints[last].iX,
       
   336             CheckMovement(iDragPoints[first], iDragPoints[last]));
       
   337     DP2_IMAGIC(_L("vector (%d,%d)"), aVector.iX, aVector.iY);
       
   338     DP0_IMAGIC(_L("CGesture::CheckFlick--"));
       
   339 
       
   340     return ret;
       
   341     }
       
   342 
       
   343 void CGesture::RunL()
       
   344     {
       
   345     DP0_IMAGIC(_L("CGesture::RunL++"));
       
   346 
       
   347     switch (iState)
       
   348         {
       
   349         case EBegan:
       
   350             DP1_IMAGIC(_L("CGesture::RunL (EBegan -> EStationary) iLongTapTime=%d"), iLongTapTime);
       
   351             iOwner->HandleGestureMovedL(iPointPreviousEvent, EGestureStationary);
       
   352             After(TTimeIntervalMicroSeconds32(iLongTapTime));
       
   353             iState = EStationary;
       
   354 
       
   355             // Update the position so that movement check occurs against 
       
   356             // last drag event, not against initial touch position
       
   357             if (iDragPoints.Count() > 1)
       
   358                 iPointPreviousEvent = iDragPoints[iDragPoints.Count()-1];
       
   359             break;
       
   360         case EStationary:
       
   361             DP0_IMAGIC(_L("CGesture::RunL (EStationary -> EStationary)"));
       
   362             iOwner->HandleGestureMovedL(iPointPreviousEvent, EGestureLongTapping);
       
   363             // record it's possible long tap if movement stays within threshold
       
   364             iFirstGesture |= EGestureLongTapping;
       
   365             // no state change
       
   366             break;
       
   367         case EMonitoring:
       
   368             DP1_IMAGIC(_L("CGesture::RunL (EMonitoring -> EEnded) iSafetyTime=%d"), iSafetyTime);
       
   369             iOwner->HandleGestureEndedL(iPointPreviousEvent, iFirstGesture);
       
   370             After(TTimeIntervalMicroSeconds32(iSafetyTime));
       
   371             iState = EEnded;
       
   372             break;
       
   373         case EEnded:
       
   374             DP0_IMAGIC(_L("CGesture::RunL (EEnded -> EWaiting)"));
       
   375             iState = EWaiting;
       
   376             break;
       
   377         default:
       
   378             DP0_IMAGIC(_L("CGesture::RunL (default)"));
       
   379             // do nothing
       
   380             break;
       
   381         }
       
   382 
       
   383     DP0_IMAGIC(_L("CGesture::RunL--"));
       
   384     }