webengine/webkitutils/rt_gesturehelper/src/gesturehelperimpl.cpp
changeset 0 dd21522fd290
child 8 7c90e6132015
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     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 the License "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 
       
    19 #include "gesturehelperimpl.h"
       
    20 
       
    21 #include <alf/alfevent.h>
       
    22 #include <e32base.h>
       
    23 #include <w32std.h>
       
    24 
       
    25 #include "gesture.h"
       
    26 #include "gesturedefs.h"
       
    27 #include "utils.h"
       
    28 #include "pointercapturer.h"
       
    29 #include "gestureeventfilter.h"
       
    30 #include "gesturehelpereventsender.h"
       
    31 #include "flogger.h"
       
    32 #include "gestureevent.h"
       
    33 
       
    34 using namespace RT_GestureHelper;
       
    35 
       
    36 namespace RT_GestureHelper
       
    37 {
       
    38 
       
    39 /// type of function in gesture helper to be called by the timer
       
    40 /// when timer triggers
       
    41 typedef void (CGestureHelperImpl::*CallbackFunctionL)();
       
    42 
       
    43 NONSHARABLE_CLASS( CCallbackTimer ) : public CTimer
       
    44     {
       
    45 public:
       
    46     /** Two-phase constructor */
       
    47     static CCallbackTimer* NewL( CGestureHelperImpl& aHelper, 
       
    48             CallbackFunctionL aCallbackFunctionL, TInt aDelay, TBool aIsEnabled )
       
    49         {
       
    50         CCallbackTimer* self = new ( ELeave ) CCallbackTimer( aHelper, 
       
    51             aCallbackFunctionL, aDelay, aIsEnabled );
       
    52         CleanupStack::PushL( self );
       
    53         self->ConstructL(); // construct base class
       
    54         CActiveScheduler::Add( self );
       
    55         CleanupStack::Pop( self );
       
    56         return self;
       
    57         }
       
    58         
       
    59     /** Destructor */
       
    60     ~CCallbackTimer()
       
    61         {
       
    62         Cancel();
       
    63         }
       
    64         
       
    65     /** Set whether sending holding events is currently enabled */
       
    66     void SetEnabled( TBool aEnabled )
       
    67         {
       
    68         iIsEnabled = aEnabled;
       
    69         // cancel in case hold timer is already running
       
    70         Cancel();
       
    71         }
       
    72         
       
    73     /** @return whether sending holding events is currently enabled */
       
    74     TBool IsEnabled() const
       
    75         {
       
    76         return iIsEnabled;
       
    77         }
       
    78         
       
    79     /** Start the timer. Calls CGestureHelperImpl::StartHoldingL upon completion */
       
    80     void Start()
       
    81         {
       
    82         // if sending hold events is disabled, do not ever start the hold timer, and 
       
    83         // hence hold events will never be triggered
       
    84         if ( iIsEnabled ) 
       
    85             {
       
    86             Cancel();
       
    87             After( iDelay );
       
    88             }
       
    89         }    
       
    90     void SetDelay(TInt aDelay) { iDelay = aDelay; }
       
    91     TInt GetDelay() { return iDelay; }
       
    92     
       
    93 private:    
       
    94     /** Constructor */
       
    95     CCallbackTimer( CGestureHelperImpl& aHelper,
       
    96         CallbackFunctionL aCallbackFunctionL, TInt aDelay, TBool aIsEnabled )
       
    97             : CTimer( EPriorityUserInput - 1 ), // give higher priority to new pointer events with - 1
       
    98                 iHelper( aHelper ), iCallbackFunctionL( aCallbackFunctionL ), 
       
    99                     iDelay( aDelay ), iIsEnabled( aIsEnabled ) 
       
   100         {
       
   101         }
       
   102         
       
   103     void RunL() // From CActive
       
   104         {
       
   105         (iHelper.*iCallbackFunctionL)();
       
   106         }
       
   107 
       
   108 private:
       
   109     /// helper object that will be called back when timer is triggered
       
   110     CGestureHelperImpl& iHelper;
       
   111     /// Function in the iHelper object call 
       
   112     CallbackFunctionL iCallbackFunctionL;
       
   113     /// How long a time to wait befor calling back after Start()
       
   114     TInt iDelay;
       
   115     /// whether sending holding events is currently enabled
       
   116     TBool iIsEnabled;
       
   117     };
       
   118 
       
   119 } // namespace GestureHelper
       
   120 
       
   121 /** 
       
   122  * @return position from event. Use this instead of using aEvent direction to
       
   123  *         avoid accidentally using TPointerEvent::iPosition
       
   124  */
       
   125 inline TPoint Position( const TPointerEvent& aEvent )
       
   126     {
       
   127     // use parent position, since the capturer is using full screen area,
       
   128     // and because the (Alfred) drag events are not local to visual even when
       
   129     // coming from the client
       
   130     
       
   131     return aEvent.iParentPosition;
       
   132     }
       
   133 
       
   134 // ----------------------------------------------------------------------------
       
   135 // Two-phase constructor
       
   136 // ----------------------------------------------------------------------------
       
   137 //
       
   138 CGestureHelperImpl* CGestureHelperImpl::NewL( MGestureObserver& aObserver )
       
   139     {
       
   140     CGestureHelperImpl* self = new ( ELeave ) CGestureHelperImpl( aObserver );
       
   141     CleanupStack::PushL( self );
       
   142     self->iEventSender = CGestureEventSender::NewL( aObserver );
       
   143     self->iDoubleTapTimer = CCallbackTimer::NewL( *self, EmitFirstTapEventL, 
       
   144             KMaxTapDuration, EFalse ); // double tap is disabled by default
       
   145     self->iHoldingTimer = CCallbackTimer::NewL( *self, StartHoldingL, 
       
   146         KHoldDuration, EFalse ); // holding is enabled by default
       
   147     
       
   148     self->iLongTouchTimer = CCallbackTimer::NewL( *self, HandleLongTouchL, 
       
   149             KLongTapDuration, ETrue ); // holding is enabled by default
       
   150     self->iPointerCapturer = CPointerCapturer::NewL();    
       
   151     self->iGesture = new ( ELeave ) CGesture();
       
   152     self->iUnusedGesture = new ( ELeave ) CGesture();
       
   153     TInt tapLimit = Mm2Pixels(KFingerSize_mm) / 2;
       
   154     self->iEventFilter = new (ELeave) CGestureEventFilter(tapLimit);
       
   155     CleanupStack::Pop( self );
       
   156     return self;
       
   157     }
       
   158 
       
   159 // ----------------------------------------------------------------------------
       
   160 // Constructor
       
   161 // ----------------------------------------------------------------------------
       
   162 //
       
   163 CGestureHelperImpl::CGestureHelperImpl( MGestureObserver& aObserver )
       
   164         : iObserver( aObserver )
       
   165     {
       
   166     }
       
   167 
       
   168 // ----------------------------------------------------------------------------
       
   169 // Destructor
       
   170 // ----------------------------------------------------------------------------
       
   171 //
       
   172 CGestureHelperImpl::~CGestureHelperImpl()
       
   173     {
       
   174     delete iDoubleTapTimer;
       
   175     delete iHoldingTimer;
       
   176     delete iGesture;
       
   177     delete iPreviousTapGesture;
       
   178     delete iUnusedGesture;
       
   179     delete iPointerCapturer;
       
   180     delete iLongTouchTimer;
       
   181     delete iEventFilter;
       
   182     delete iEventSender;
       
   183     }
       
   184     
       
   185 
       
   186 // ----------------------------------------------------------------------------
       
   187 // SetHoldingEnabled
       
   188 // ----------------------------------------------------------------------------
       
   189 //
       
   190 void CGestureHelperImpl::SetHoldingEnabled( TBool aEnabled )
       
   191     {
       
   192     iHoldingTimer->SetEnabled( aEnabled );
       
   193     }
       
   194 
       
   195 // ----------------------------------------------------------------------------
       
   196 // IsHoldingEnabled
       
   197 // ----------------------------------------------------------------------------
       
   198 //
       
   199 TBool CGestureHelperImpl::IsHoldingEnabled() const
       
   200     {
       
   201     return iHoldingTimer->IsEnabled();
       
   202     }
       
   203 
       
   204 // ----------------------------------------------------------------------------
       
   205 // SetHoldingEnabled
       
   206 // ----------------------------------------------------------------------------
       
   207 //
       
   208 void CGestureHelperImpl::SetDoubleTapEnabled( TBool aEnabled )
       
   209     {
       
   210     iDoubleTapTimer->SetEnabled( aEnabled );
       
   211     }
       
   212 
       
   213 // ----------------------------------------------------------------------------
       
   214 // IsHoldingEnabled
       
   215 // ----------------------------------------------------------------------------
       
   216 //
       
   217 TBool CGestureHelperImpl::IsDoubleTapEnabled() const
       
   218     {
       
   219     return iDoubleTapTimer->IsEnabled();
       
   220     }
       
   221     
       
   222 // ----------------------------------------------------------------------------
       
   223 // InitAlfredPointerEventCaptureL
       
   224 // ----------------------------------------------------------------------------
       
   225 //
       
   226 void CGestureHelperImpl::InitAlfredPointerCaptureL( CAlfEnv& aEnv, 
       
   227         CAlfDisplay& aDisplay, TInt aFreeControlGroupId )
       
   228     {
       
   229     iPointerCapturer->InitForAlfredL(*this, aEnv, aDisplay, aFreeControlGroupId );
       
   230     }
       
   231 
       
   232 // ----------------------------------------------------------------------------
       
   233 // Reset state
       
   234 // ----------------------------------------------------------------------------
       
   235 //
       
   236 void CGestureHelperImpl::Reset()
       
   237     {
       
   238     iHoldingTimer->Cancel();
       
   239     iLongTouchTimer->Cancel();
       
   240     iGesture->Reset();
       
   241     iPointerCapturer->Stop();
       
   242     }
       
   243 
       
   244 /** 
       
   245  * Helper function that calls Reset on the pointer to CGestureHelperImpl
       
   246  */
       
   247 static void ResetHelper( TAny* aHelper )
       
   248     {
       
   249     static_cast< CGestureHelperImpl* >( aHelper )->Reset();
       
   250     }
       
   251 
       
   252 // ----------------------------------------------------------------------------
       
   253 // Handle a pointer event
       
   254 // ----------------------------------------------------------------------------
       
   255 //
       
   256 TBool CGestureHelperImpl::HandlePointerEventL( const TPointerEvent& aEvent )
       
   257     {     
       
   258     TInt filterReason;
       
   259     SetLastEventTime();
       
   260     if (!iEventFilter->FilterDrag(aEvent, iLastEventTime, filterReason))
       
   261         {
       
   262         iGesture->SetVisual( NULL );
       
   263         return noneAlf_HandlePointerEventL( aEvent );
       
   264         }
       
   265     else
       
   266         {
       
   267         /*
       
   268         TBuf<10> num;
       
   269         num.Num( filterReason );
       
   270         TBuf<128> str;
       
   271         str.AppendFormat(_L("Filter reason: %d"), filterReason);
       
   272         RFileLogger::Write( _L("gh"), _L("gh.txt"), EFileLoggingModeAppend, str);
       
   273         */
       
   274         return EFalse;
       
   275         }
       
   276     }
       
   277 
       
   278 // ----------------------------------------------------------------------------
       
   279 // OfferEventL
       
   280 // ----------------------------------------------------------------------------
       
   281 //
       
   282 TBool CGestureHelperImpl::OfferEventL( const TAlfEvent& aEvent )
       
   283     {
       
   284     if ( aEvent.IsPointerEvent() )
       
   285         {
       
   286         return HandlePointerEventL( aEvent.PointerEvent(), aEvent.Visual() );
       
   287         }
       
   288     return EFalse;
       
   289     }
       
   290 
       
   291 
       
   292 
       
   293 
       
   294 
       
   295 
       
   296 // ----------------------------------------------------------------------------
       
   297 // Handle a pointer event
       
   298 // ----------------------------------------------------------------------------
       
   299 //
       
   300 
       
   301 
       
   302 TBool CGestureHelperImpl::noneAlf_HandlePointerEventL( const TPointerEvent& aEvent)
       
   303     {
       
   304     
       
   305     switch ( aEvent.iType )
       
   306         {
       
   307         case TPointerEvent::EButton1Down:
       
   308             {
       
   309             
       
   310             iPointerCapturer->StartL();
       
   311             HandleTouchDownL(aEvent);
       
   312             break;
       
   313             }
       
   314         case TPointerEvent::EDrag:
       
   315             {
       
   316             HandleMoveL(aEvent);
       
   317             break;
       
   318             }
       
   319         case TPointerEvent::EButton1Up:
       
   320             {
       
   321             CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) );
       
   322             if (KErrNone == AddPoint( aEvent ))
       
   323                 {
       
   324                 HandleTouchUpL(aEvent);
       
   325                 }
       
   326             else
       
   327                 {
       
   328                 EmitCancelEventL();
       
   329                 }
       
   330             CleanupStack::PopAndDestroy( this ); 
       
   331             break;
       
   332             }
       
   333         default:
       
   334             break;
       
   335         }
       
   336     return ETrue;
       
   337     }
       
   338 
       
   339 
       
   340 TBool CGestureHelperImpl::HandlePointerEventL( const TPointerEvent& aEvent,
       
   341         CAlfVisual* aVisual )
       
   342     {  
       
   343     // filter out events that do not start with button down. It is a stray
       
   344     // event from another visual
       
   345     if ( IsIdle() && aEvent.iType != TPointerEvent::EButton1Down )
       
   346         {
       
   347         return EFalse; // don't consume
       
   348         }
       
   349     
       
   350     switch ( aEvent.iType )
       
   351         {
       
   352         case TPointerEvent::EButton1Down:
       
   353             // If no up event was received during previous gesture, cancel 
       
   354             // previous event and reset state
       
   355             if ( !IsIdle() )
       
   356                 {
       
   357                 // ambiguous what is the right thing when "cancel" event leaves
       
   358                 // and "start" does not. Leaving for cancel *after* "start" could 
       
   359                 // be unexpected to client, as client would have handled start 
       
   360                 // event successfully. Assume that leaving upon cancellation 
       
   361                 // can be ignored.
       
   362                 TRAP_IGNORE( EmitCancelEventL() );
       
   363                 Reset();  
       
   364                 }
       
   365             // as long as down event of a double tap comes within the double 
       
   366             // tap timeout, it does not matter how long the user keeps the finger
       
   367             // pressed for the gesture to be a double tap. Therefore, cancel
       
   368             // the timeout, as it is no longer relevant. (Of course, this call
       
   369             // will only do something if the timer is actually running, which
       
   370             // is only if received a tap event very recently.)
       
   371             iDoubleTapTimer->Cancel();
       
   372             // adding the first point implicitly makes the state "not idle"
       
   373             AddPointL( aEvent );
       
   374             iGesture->SetVisual( aVisual );
       
   375             // if pointer capturer leaves, the remaining pointer events will
       
   376             // not be captured if stylus is dragged outside the capturing visual
       
   377             // an error note will be shown, so the potential problem is irrelevant,
       
   378             // assuming client does not (incorrectly) block the leave from reaching 
       
   379             // the framework
       
   380             iPointerCapturer->StartL();
       
   381             // Delay emitting a down event _until_ it is known that this beginning 
       
   382             // gesture is _not_ the second tap of a double tap event.
       
   383             // iPreviousTapGesture is only non-null if very recently received 
       
   384             // a tap event and double tap is enabled. 
       
   385             if ( !iPreviousTapGesture )
       
   386                 {
       
   387                 EmitEventL( *iGesture );
       
   388                 }
       
   389             // else delay emitting an event, as it might be a double tap 
       
   390             // (allow the second tap of a double tap to be anywhere, so don't check
       
   391             // for start pos here)
       
   392             break;
       
   393             
       
   394         case TPointerEvent::EDrag:
       
   395             // While stylus down, the same event is received repeatedly
       
   396             // even if stylus does not move. Filter out by checking if point 
       
   397             // is the same as the latest point
       
   398             if ( !iGesture->IsLatestPoint( Position( aEvent ) ) )
       
   399                 {
       
   400                 AddPointL( aEvent );
       
   401 
       
   402                 // as long as the starting gesture is seen as a tap, do not emit any
       
   403                 // drag events
       
   404                 if ( !iGesture->IsTap() )
       
   405                     {
       
   406                     // if there is a previous tap gesture, getting drag events means that
       
   407                     // the previous gesture is not a double tap. So emit the previous gesture.
       
   408                     if ( iPreviousTapGesture )
       
   409                         {
       
   410                         // this is a second gesture after a tap (double tap is enabled)
       
   411                         EmitFirstTapEventL();
       
   412                         // emit down event for the current gesture (since its down was delayed, until
       
   413                         // it was to be known if the event is a tap. That is known now.)
       
   414                         EmitStartEventL( *iGesture );
       
   415                         }
       
   416                     // restart holding timer every time the current stylus pos changes
       
   417                     StartHoldingTimer( aEvent );
       
   418                     // emit the drag event to client
       
   419                     EmitEventL( *iGesture );
       
   420                     }
       
   421                 // else: do not emit drag events until it is known that the gesture is not a tap
       
   422                 // (or the second tap of double tap)
       
   423                 }
       
   424             break;
       
   425 
       
   426         case TPointerEvent::EButton1Up:
       
   427             // reset in case the down event for next gesture is not received for a reason 
       
   428             // in client, and instead drag or up events are received. 
       
   429             // reset via cleanup stack to ensure Reset is run even if
       
   430             // observer leaves
       
   431             CleanupStack::PushL( TCleanupItem( &ResetHelper, this ) );
       
   432             // if adding of the point fails, notify client with a 
       
   433             // cancelled event. It would be wrong to send another
       
   434             // gesture code when the up point is not known
       
   435             if ( KErrNone == AddPoint( aEvent ) )
       
   436                 {
       
   437                 
       
   438                 // if the gesture is a tap, the gesture is either the first tap of a _potential_
       
   439                 // double tap, or the second tap of a double tap
       
   440                 if ( iDoubleTapTimer->IsEnabled() && iGesture->IsTap() )
       
   441                     {
       
   442                     __ASSERT_DEBUG( !iGesture->IsHolding(), Panic( EGesturePanicIllegalLogic ) );
       
   443                     if ( !iPreviousTapGesture )
       
   444                         {
       
   445                         // First tap. Delay emitting its code evemt and released events until it is known
       
   446                         // whether the tap is a double tap
       
   447                         iPreviousTapGesture = iGesture;
       
   448                         iGesture = NewGesture();
       
   449                         iDoubleTapTimer->Start(); 
       
   450                         }
       
   451                     else
       
   452                         {
       
   453                         // This is a second tap of a double tap. Do not emit anything for the second
       
   454                         // tap. Only down event has been emitted for the first tap. Emit the code 
       
   455                         // event (double tap) and released for the first tap.
       
   456                         iPreviousTapGesture->SetDoubleTap();
       
   457                         EmitFirstTapEventL();
       
   458                         }
       
   459                     }
       
   460                 
       
   461                 else 
       
   462                     {
       
   463                     // modified iGesture to be "released"
       
   464                     CompleteAndEmitL( *iGesture );
       
   465                     }
       
   466                 }
       
   467             else
       
   468                 { // adding a point failed
       
   469                 EmitCancelEventL();
       
   470                 }
       
   471             // reset state
       
   472             CleanupStack::PopAndDestroy( this ); 
       
   473             break;
       
   474             
       
   475         default:
       
   476             break;
       
   477         }
       
   478     return ETrue; // consume
       
   479     }
       
   480 
       
   481 
       
   482 TBool CGestureHelperImpl::IsMovementGesture(TGestureCode aCode)
       
   483     {
       
   484     return (aCode == EGestureDrag || aCode == EGestureFlick || aCode == EGestureSwipeUp ||
       
   485             aCode == EGestureSwipeDown || aCode == EGestureSwipeRight || aCode == EGestureSwipeLeft);
       
   486     }
       
   487 
       
   488 void CGestureHelperImpl::HandleLongTouchL()
       
   489     {
       
   490     iDoubleTapTimer->Cancel();
       
   491     iGesture->SetLongTap(ETrue);
       
   492     iGesture->SetComplete();
       
   493     TPoint startPos = iGesture->StartPos();
       
   494     EmitEventL(*iGesture);
       
   495     iGesture->Reset();
       
   496     iGesture->AddPoint( startPos, GetLastEventTime() );
       
   497     }
       
   498 
       
   499 void CGestureHelperImpl::HandleTouchDownL(const TPointerEvent& aEvent)
       
   500     {
       
   501     TGestureCode prevCode = iGesture->PreviousGestureCode();
       
   502     if (prevCode == EGestureStart) return;
       
   503     AddPointL( aEvent );
       
   504     
       
   505     iLongTouchTimer->Start();
       
   506     if (!iDoubleTapTimer->IsActive())
       
   507         {
       
   508             EmitEventL( *iGesture );
       
   509         }
       
   510     }
       
   511 
       
   512 void CGestureHelperImpl::HandleMoveL(const TPointerEvent& aEvent)
       
   513     {
       
   514     if (iGesture->IsLatestPoint( iGesture->Visual() ? Position ( aEvent ) : aEvent.iPosition)) return; // I'm not sure we need this
       
   515     //Cancel double tap time - it's neither tap nor double tap 
       
   516     iDoubleTapTimer->Cancel();
       
   517     iLongTouchTimer->Cancel();
       
   518     
       
   519     TBool isFirstPoint = IsIdle();
       
   520     
       
   521     AddPointL( aEvent );
       
   522     
       
   523     if (iPreviousTapGesture)
       
   524         {
       
   525         RecycleGesture(iPreviousTapGesture);
       
   526         }
       
   527     
       
   528     if (!isFirstPoint)
       
   529         {
       
   530         EmitEventL( *iGesture );
       
   531         }
       
   532     }
       
   533 
       
   534 void CGestureHelperImpl::HandleTouchUpL(const TPointerEvent& /*aEvent*/)
       
   535     {
       
   536     TGestureCode prevCode = iGesture->PreviousGestureCode();
       
   537     iLongTouchTimer->Cancel();
       
   538     iDoubleTapTimer->Cancel();
       
   539     TInt64 fromLastTouchUp = iLastEventTime.MicroSecondsFrom(iLastTouchUpTime).Int64();
       
   540     TInt64 fromLastDoubleTap = iLastEventTime.MicroSecondsFrom(iLastDoubleTapTime).Int64();
       
   541     /*
       
   542     TBuf<1024> str;
       
   543     str.AppendFormat(_L("fromLastTouchUp: %d, "), fromLastTouchUp);
       
   544     str.AppendFormat(_L("fromLastDoubleTap: %d, "), fromLastTouchUp);
       
   545     str.AppendFormat(_L("iPreviousTapGesture: %d, "), iPreviousTapGesture);
       
   546     RFileLogger::Write( _L("gh"), _L("gh.txt"), EFileLoggingModeAppend, str);
       
   547     */
       
   548     if ( prevCode == EGestureLongTap )
       
   549         {
       
   550         EmitReleasedEventL();
       
   551         }
       
   552     else if (IsMovementGesture(prevCode) || 
       
   553              !iDoubleTapTimer->IsEnabled() /* || !iGesture->IsTap()*/ ) 
       
   554         {
       
   555         iGesture->SetComplete();
       
   556         EmitEventL(*iGesture);
       
   557         }
       
   558     
       
   559     else 
       
   560         {
       
   561         if ( iPreviousTapGesture && 
       
   562          (fromLastTouchUp > KDoubleTapMinActivationInterval) &&       
       
   563          (fromLastTouchUp < KDoubleTapMaxActivationInterval) &&
       
   564          (fromLastDoubleTap > KDoubleTapIdleInterval))
       
   565             {
       
   566             // it's a double tap
       
   567             iLastTouchUpTime = iLastEventTime;
       
   568             iLastDoubleTapTime = iLastEventTime;
       
   569             EmitDoubleTapEventL();
       
   570             }
       
   571         else
       
   572             {
       
   573             // it's a first tap
       
   574             iLastTouchUpTime = iLastEventTime;
       
   575             if (iPreviousTapGesture)
       
   576                 {
       
   577                    RecycleGesture(iPreviousTapGesture);
       
   578                 }
       
   579                         
       
   580             iPreviousTapGesture = iGesture;
       
   581             iGesture = NewGesture();
       
   582             iDoubleTapTimer->Start(); 
       
   583             }
       
   584         }
       
   585     }
       
   586 
       
   587 
       
   588 
       
   589 void CGestureHelperImpl::EmitDoubleTapEventL()
       
   590     {
       
   591     iPreviousTapGesture->SetDoubleTap();
       
   592     EmitFirstTapEventL();
       
   593     }
       
   594 
       
   595 
       
   596 void CGestureHelperImpl::EmitReleasedEventL()
       
   597     {
       
   598     iGesture->SetComplete();
       
   599     iGesture->SetReleased();
       
   600     EmitEventL(*iGesture);
       
   601     }
       
   602 
       
   603 
       
   604 // ----------------------------------------------------------------------------
       
   605 // Is the helper idle?
       
   606 // inline ok in cpp file for a private member function
       
   607 // ----------------------------------------------------------------------------
       
   608 //
       
   609 inline TBool CGestureHelperImpl::IsIdle() const
       
   610     {
       
   611     return iGesture->IsEmpty();
       
   612     }
       
   613 
       
   614 // ----------------------------------------------------------------------------
       
   615 // Add a point to the sequence of points that together make up the gesture
       
   616 // inline ok in cpp file for a private member function
       
   617 // ----------------------------------------------------------------------------
       
   618 //
       
   619 inline void CGestureHelperImpl::AddPointL( const TPointerEvent& aEvent )
       
   620     {
       
   621     User::LeaveIfError( AddPoint( aEvent ) );
       
   622     }
       
   623 
       
   624 // ----------------------------------------------------------------------------
       
   625 // Add a point to the sequence of points that together make up the gesture
       
   626 // inline ok in cpp file for a private member function
       
   627 // ----------------------------------------------------------------------------
       
   628 //
       
   629 inline TInt CGestureHelperImpl::AddPoint( const TPointerEvent& aEvent )
       
   630     {
       
   631     TPoint pos = iGesture->Visual() ? Position ( aEvent ) : aEvent.iPosition;
       
   632     return iGesture->AddPoint( pos, GetLastEventTime() );
       
   633     }
       
   634 
       
   635 // ----------------------------------------------------------------------------
       
   636 // StartHoldingTimer
       
   637 // ----------------------------------------------------------------------------
       
   638 //
       
   639 void CGestureHelperImpl::StartHoldingTimer( const TPointerEvent& aNewEvent )
       
   640     {
       
   641     if ( !( iGesture->IsHolding() ||
       
   642             iGesture->IsNearHoldingPoint( Position( aNewEvent ) ) ) )
       
   643         {
       
   644         // restart hold timer, since pointer has moved
       
   645         iHoldingTimer->Start();
       
   646         // Remember the point in which holding was started
       
   647         iGesture->SetHoldingPoint();
       
   648         }
       
   649     }
       
   650 
       
   651 /** 
       
   652  * Helper function that calls ContinueHolding on the pointer to TGesture
       
   653  */
       
   654 static void ContinueHolding( TAny* aGesture )
       
   655     {
       
   656     static_cast< CGesture* >( aGesture )->ContinueHolding();
       
   657     }
       
   658 
       
   659 // ----------------------------------------------------------------------------
       
   660 // Add a point to the sequence of points that together make up the gesture
       
   661 // ----------------------------------------------------------------------------
       
   662 //
       
   663 void CGestureHelperImpl::StartHoldingL()
       
   664     {
       
   665     // hold & tap event is specifically filtered out. Use case: in list fast 
       
   666     // scrolling activation (e.g. enhanced coverflow), tap & hold should not
       
   667     // start fast scroll. In addition, after long tap on start position,
       
   668     // drag and drag & hold swiping should emit normal swipe and swipe&hold
       
   669     // events. Therefore, tap & hold is not supported.
       
   670     __ASSERT_DEBUG( !iGesture->IsTap() && !iPreviousTapGesture, Panic( EGesturePanicIllegalLogic ) );
       
   671     
       
   672     // holding has just started, and gesture code should be provided to client.
       
   673     // set gesture state so that it produces a gesture code (other than drag)
       
   674     iGesture->StartHolding();
       
   675     
       
   676     // create an item in the cleanup stack that will set the gesture state
       
   677     // to holding-was-started-earlier state. NotifyL may leave, but the
       
   678     // holding-was-started-earlier state must still be successfully set,
       
   679     // otherwise, the holding gesture code will be sent twice
       
   680     CleanupStack::PushL( TCleanupItem( &ContinueHolding, iGesture ) );
       
   681     
       
   682     EmitEventL( *iGesture );
       
   683     
       
   684     // set holding state to "post holding"
       
   685     CleanupStack::PopAndDestroy( iGesture );
       
   686     }
       
   687 
       
   688 // ----------------------------------------------------------------------------
       
   689 // RecyclePreviousTapGesture
       
   690 // ----------------------------------------------------------------------------
       
   691 //
       
   692 void CGestureHelperImpl::RecyclePreviousTapGesture( TAny* aSelf )
       
   693     {
       
   694     CGestureHelperImpl& self = *reinterpret_cast<CGestureHelperImpl*>( aSelf );
       
   695     self.RecycleGesture( self.iPreviousTapGesture );
       
   696     }
       
   697 
       
   698 // ----------------------------------------------------------------------------
       
   699 // Emit the remainder of the previous tap event (tap + released)
       
   700 // ----------------------------------------------------------------------------
       
   701 //
       
   702 void CGestureHelperImpl::EmitFirstTapEventL()
       
   703     {
       
   704     // when this function is called, a tap has turned out to _not_ be a double tap
       
   705     __ASSERT_DEBUG( IsDoubleTapEnabled(), Panic( EGesturePanicIllegalLogic ) );
       
   706     __ASSERT_DEBUG( iPreviousTapGesture, Panic( EGesturePanicIllegalLogic ) );
       
   707     
       
   708     iDoubleTapTimer->Cancel();
       
   709     
       
   710     // ensure previous tap gesture is reset even if client leaves
       
   711     CleanupStack::PushL( TCleanupItem( &RecyclePreviousTapGesture, this ) );
       
   712     
       
   713     CompleteAndEmitL( *iPreviousTapGesture );
       
   714     
       
   715     // recycle the emitted gesture 
       
   716     CleanupStack::PopAndDestroy( this ); 
       
   717     }
       
   718 
       
   719 // ----------------------------------------------------------------------------
       
   720 // EmitStartEventL
       
   721 // ----------------------------------------------------------------------------
       
   722 //
       
   723 void CGestureHelperImpl::EmitStartEventL( const CGesture& aGesture )    
       
   724     {
       
   725     CGesture* startGesture = aGesture.AsStartEventLC();
       
   726     EmitEventL( *startGesture );
       
   727     CleanupStack::PopAndDestroy( startGesture );    
       
   728     }
       
   729     
       
   730 // ----------------------------------------------------------------------------
       
   731 // EmitCompletionEventsL
       
   732 // ----------------------------------------------------------------------------
       
   733 //
       
   734 void CGestureHelperImpl::CompleteAndEmitL( CGesture& aGesture )
       
   735     {
       
   736     aGesture.SetComplete();
       
   737     // send gesture code if holding has not been started. If holding has 
       
   738     // been started, client has already received a "hold swipe left" e.g. event, in which
       
   739     // case don't another "swipe left" event
       
   740     if ( !aGesture.IsHolding() )
       
   741         {
       
   742         // if client leaves, the state is automatically reset.
       
   743         // In this case the client will not get the released event
       
   744         EmitEventL( aGesture ); 
       
   745         }
       
   746     
       
   747     // send an event that stylus was lifted
       
   748     aGesture.SetReleased();
       
   749     EmitEventL( aGesture ); 
       
   750     }
       
   751     
       
   752 // ----------------------------------------------------------------------------
       
   753 // EmitCancelEventL
       
   754 // ----------------------------------------------------------------------------
       
   755 //
       
   756 void CGestureHelperImpl::EmitCancelEventL()
       
   757     {
       
   758     iDoubleTapTimer->Cancel();
       
   759 
       
   760     // ensure previous tap gesture is reset even if client leaves
       
   761     CleanupStack::PushL( TCleanupItem( &RecyclePreviousTapGesture, this ) );
       
   762 
       
   763     CGesture& gestureToCancel = iPreviousTapGesture ? *iPreviousTapGesture : *iGesture;
       
   764     gestureToCancel.SetCancelled();
       
   765     EmitEventL( gestureToCancel );
       
   766     
       
   767     // recycle the emitted gesture 
       
   768     CleanupStack::PopAndDestroy( this ); 
       
   769     }
       
   770 
       
   771 // ----------------------------------------------------------------------------
       
   772 // Notify observer
       
   773 // ----------------------------------------------------------------------------
       
   774 //
       
   775 void CGestureHelperImpl::EmitEventL( const CGesture& aGesture )
       
   776     {
       
   777     // deallocation of the event is happening in CGestureEventSender::RunL() 
       
   778     CGestureEvent* event = new(ELeave) CGestureEvent();
       
   779     event->iCode = const_cast<CGesture&>(aGesture).Code(MGestureEvent::EAxisBoth);
       
   780     event->iCurrPos = aGesture.CurrentPos();
       
   781     event->iDistance = aGesture.Distance();
       
   782     event->iStartPos = aGesture.StartPos();
       
   783     event->iIsHolding = aGesture.IsHolding();
       
   784     event->iSpeed = aGesture.Speed();
       
   785     event->iVisual = aGesture.Visual();
       
   786     iEventSender->AddEvent(event);
       
   787     }
       
   788 
       
   789 // ----------------------------------------------------------------------------
       
   790 // Return a fresh gesture from the gesture pool (pool of one gesture)
       
   791 // ----------------------------------------------------------------------------
       
   792 //
       
   793 CGesture* CGestureHelperImpl::NewGesture()
       
   794     {
       
   795     __ASSERT_DEBUG( iUnusedGesture, Panic( EGesturePanicIllegalLogic ) ); // pool should no be empty
       
   796     
       
   797     iUnusedGesture->Reset();
       
   798     CGesture* freshGesture = iUnusedGesture;
       
   799     iUnusedGesture = NULL;
       
   800     return freshGesture;
       
   801     }
       
   802 
       
   803 // ----------------------------------------------------------------------------
       
   804 // Return a fresh gesture from the gesture pool (pool of one gesture)
       
   805 // ----------------------------------------------------------------------------
       
   806 //
       
   807 void CGestureHelperImpl::RecycleGesture( CGesture*& aGesturePointer )
       
   808     {
       
   809     // only one object fits into the pool, and that should currently be enough
       
   810     // one pointer must be null, one non-null
       
   811     __ASSERT_DEBUG( !iUnusedGesture != !aGesturePointer, Panic( EGesturePanicIllegalLogic ) );
       
   812     if ( aGesturePointer )
       
   813         {
       
   814         iUnusedGesture = aGesturePointer;
       
   815         aGesturePointer = NULL;
       
   816         }
       
   817     }