akntouchgesturefw/src/akntouchgesturefwpinchrecognizer.cpp
changeset 0 2f259fa3e83a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/akntouchgesturefw/src/akntouchgesturefwpinchrecognizer.cpp	Tue Feb 02 01:00:49 2010 +0200
@@ -0,0 +1,624 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Pinch touch gesture recognizer.
+*
+*/
+
+#include "akntouchgesturefwdefs.h"
+#include "akntouchgesturefwevent.h"
+#include "akntouchgesturefwpinchrecognizer.h"
+#include "akntouchgesturefwsettings.h"
+
+using namespace AknTouchGestureFw;
+
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+CAknTouchGestureFwPinchRecognizer* CAknTouchGestureFwPinchRecognizer::NewL(
+    CAknTouchGestureFwRecognitionEngine& aEngine )
+    {
+    CAknTouchGestureFwPinchRecognizer* self =
+        CAknTouchGestureFwPinchRecognizer::NewLC( aEngine );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+CAknTouchGestureFwPinchRecognizer* CAknTouchGestureFwPinchRecognizer::NewLC(
+        CAknTouchGestureFwRecognitionEngine& aEngine )
+    {
+    CAknTouchGestureFwPinchRecognizer* self
+        = new ( ELeave ) CAknTouchGestureFwPinchRecognizer( aEngine );
+    CleanupStack::PushL( self );
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Destructor
+// ---------------------------------------------------------------------------
+//
+CAknTouchGestureFwPinchRecognizer::~CAknTouchGestureFwPinchRecognizer()
+    {
+    }
+
+
+// ---------------------------------------------------------------------------
+// Returns the pinch gesture group.
+// ---------------------------------------------------------------------------
+//
+TAknTouchGestureFwGroup CAknTouchGestureFwPinchRecognizer::GestureGroup() const
+    {
+    return EAknTouchGestureFwGroupPinch;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Cancels the gesture recognition.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::CancelRecognizing()
+    {
+    if ( iContinuousFeedback )
+        {
+        StopContinuousFeedback();
+        iContinuousFeedback = EFalse;
+        }
+    
+    if ( iPinchDetected )
+        {
+        // We ignore the possible leave in order to ensure that the
+        // state gets reset.
+        TRAP_IGNORE( SendPinchEventL( EAknTouchGestureFwStop, 0 ) );
+        }
+    Reset();
+    }
+
+
+// ---------------------------------------------------------------------------
+// Handles single-touch pointer events.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::HandleSinglePointerEventL(
+        const TPointerEventData& /*aPointerData*/ )
+    {
+    // No single touch event handling in pinch recognizer
+    }
+
+
+// ---------------------------------------------------------------------------
+// Handles multi-touch pointer events.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::HandleMultiPointerEventL(
+        const TPointerEventData& aPointerData,
+        const TPoint& aFirstPointerPosition,
+        const TPoint& aSecondPointerPosition )
+    {
+    // Handle event
+    switch ( aPointerData.iPointerEvent.iType )
+        {
+        case TPointerEvent::EButton1Down:
+            {
+            StartMultiRecognizing( 
+                aFirstPointerPosition,
+                aSecondPointerPosition );
+            break;
+            }
+            
+        case TPointerEvent::EDrag:
+            {
+            MultiRecognizeL( 
+                aPointerData.iTimeStamp,
+                aFirstPointerPosition,
+                aSecondPointerPosition );
+            break;
+            }
+        case TPointerEvent::EButton1Up:
+            {
+            CompleteMultiRecognizingL( aPointerData.iTimeStamp );
+            break;
+            }
+        default:
+            {
+            break;
+            }
+        }
+    }
+
+
+// ---------------------------------------------------------------------------
+// C++ constructor.
+// ---------------------------------------------------------------------------
+//
+CAknTouchGestureFwPinchRecognizer::CAknTouchGestureFwPinchRecognizer(
+    CAknTouchGestureFwRecognitionEngine& aEngine )
+    : CAknTouchGestureFwBaseRecognizer( aEngine )
+    {
+    Reset();
+    }
+
+
+// ---------------------------------------------------------------------------
+// Starts recognizing the pinch gesture.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::StartMultiRecognizing(
+        const TPoint& aFirstPointerPos,
+        const TPoint& aSecondPointerPos )
+    {
+    // Initialize members for multi recognition.
+    Reset();
+    
+    iPinchRect = CalculateBoundingRect( aFirstPointerPos, aSecondPointerPos );
+
+    TTouchFeedbackType feedbackType( FeedbackType( EAknTouchGestureFwPinch ) );
+    if ( feedbackType )
+        {
+        ImmediateFeedback( ETouchFeedbackSensitive, feedbackType );
+        ImmediateFeedback( ETouchFeedbackSensitive, feedbackType );
+        }
+    }
+
+
+// ---------------------------------------------------------------------------
+// Continues recognizing the pinch gesture.
+// Called for every drag-event when more than one pointer is down.
+// Threshold handling for pinch is implemented here.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::MultiRecognizeL(
+        const TTime& aEventTime,
+        const TPoint& aFirstPointerPos,
+        const TPoint& aSecondPointerPos )
+    {    
+    const TInt pinchThreshold = iPinchDetected ? 
+        PinchMovementThreshold() : PinchInitialThreshold();
+
+    // We use boundingRect instead of individual positions, because
+    // those positions are not stable with all HW. Instead, bounding rect is.
+    TRect boundingRect = 
+        CalculateBoundingRect( aFirstPointerPos, aSecondPointerPos );
+
+    // Snap to iPinchRect values.
+    TRect normalizedRect = boundingRect;
+    NormalizeValue( normalizedRect.iTl.iX, iPinchRect.iTl.iX, pinchThreshold );
+    NormalizeValue( normalizedRect.iTl.iY, iPinchRect.iTl.iY, pinchThreshold );
+    NormalizeValue( normalizedRect.iBr.iX, iPinchRect.iBr.iX, pinchThreshold );
+    NormalizeValue( normalizedRect.iBr.iY, iPinchRect.iBr.iY, pinchThreshold );
+
+    // Determine if width or height should be ignored. 
+    // If length of a dimension is close to zero, it's assumed that 
+    // those values may be unreliable.
+    const TInt dimensionThreshold = PinchDimensionThreshold();
+    TBool ignoreWidth =
+        ( boundingRect.Width() < dimensionThreshold ) ||
+        ( normalizedRect.Width() < dimensionThreshold ) ||
+        ( iPinchRect.Width() < dimensionThreshold );
+    TBool ignoreHeight =
+        ( boundingRect.Height() < dimensionThreshold ) ||
+        ( normalizedRect.Height() < dimensionThreshold ) ||
+        ( iPinchRect.Height() < dimensionThreshold );
+
+    // Calculate change in width & height.
+    TInt widthDelta = ( normalizedRect.Width() - iPinchRect.Width() );
+    TInt heightDelta = ( normalizedRect.Height() - iPinchRect.Height() );
+       
+    TBool handleEvent = EFalse;
+    if ( normalizedRect != iPinchRect )
+        {
+        handleEvent = ETrue;
+        iPinchRect = normalizedRect;
+        }
+
+    if ( ignoreWidth )
+        {
+        widthDelta = 0;
+        iStoredWidthDelta = 0;
+        }
+    
+    if ( ignoreHeight )
+        {
+        heightDelta = 0;
+        iStoredHeightDelta = 0;
+        }
+
+    // Pinch values are sent in a slight delay in order to prevent
+    // sending of incorrect values when pointer coordinates are
+    // snapped to horizontal/vertical line as snapping tends to
+    // happen with two events (one for each pointer).
+    //
+    // Events are sent here.
+
+    TInt oldStoredWidthDelta = iStoredWidthDelta;
+    TInt oldStoredHeightDelta = iStoredHeightDelta;
+
+    if ( oldStoredWidthDelta || oldStoredHeightDelta )
+        {
+        iStoredWidthDelta = 0;
+        iStoredHeightDelta = 0;
+    
+        TInt movement = 0;
+        
+        if ( CheckDurationThreshold( aEventTime ) )
+            {
+            movement = 
+                CalculateMovement( 
+                    oldStoredWidthDelta,
+                    oldStoredHeightDelta );
+            }
+            
+    
+        if ( movement )
+            {
+            if ( iPinchDetected )
+                {
+                SendPinchEventL( EAknTouchGestureFwOn, movement );
+                }
+            else
+                {
+                iPinchDetected = ETrue;
+                SendPinchEventL( EAknTouchGestureFwStart, movement );
+                }
+            }
+        }
+
+    if ( !handleEvent )
+        {
+        // No change, no need to continue
+        return;
+        }
+    
+    // Now it is guaranteed that widthDelta or heightDelta does not equal
+    // to zero.
+
+    // Update width direction tracer
+    if ( !ignoreWidth )
+        {
+        iPinchWidthTracer.Update( 
+            widthDelta, 
+            PinchDirectionChangeSensitivity() );
+        }
+    else
+        {
+        // Cannot say anything about this direction - snapped together
+        iPinchWidthTracer.Reset();
+        }
+    
+    // Update height direction tracer
+    if ( !ignoreHeight )
+        {
+        iPinchHeightTracer.Update( 
+            heightDelta, 
+            PinchDirectionChangeSensitivity() );
+        }
+    else
+        {
+        // Cannot say anything about this direction - snapped together
+        iPinchHeightTracer.Reset();
+        }
+
+    // Reset direction tracer if height changes but width stays the same.
+    if ( widthDelta )
+        {
+        iResetPinchWidthTracer = 0;
+        }
+
+    if ( !widthDelta && heightDelta )
+        {
+        if ( iResetPinchWidthTracer >= PinchDirectionResetSensitivity() )
+            {
+            iPinchWidthTracer.Reset();
+            iResetPinchWidthTracer = 0;
+            }
+        else
+            {
+            iResetPinchWidthTracer++;
+            }
+        }
+
+    // Reset direction tracer if width changes but height stays the same.
+    if ( heightDelta )
+        {
+        iResetPinchHeightTracer = 0;
+        }
+            
+    if ( !heightDelta && widthDelta )
+        {
+        if ( iResetPinchHeightTracer >= PinchDirectionResetSensitivity() )
+            {
+            iPinchHeightTracer.Reset();
+            iResetPinchHeightTracer = 0;
+            }
+        else
+            {
+            iResetPinchHeightTracer++;
+            }
+        }
+
+    TInt pinchWidthDelta = 0;
+    TInt pinchHeightDelta = 0;
+
+    if ( widthDelta >= 0 && heightDelta >= 0 )
+        {
+        TDirectionTracer::TDirection widthDir = 
+            iPinchWidthTracer.Direction();
+        TDirectionTracer::TDirection heightDir = 
+            iPinchHeightTracer.Direction();
+                
+        if ( ( widthDir == TDirectionTracer::EDirectionPositive && 
+               heightDir != TDirectionTracer::EDirectionNegative ) || 
+             ( heightDir == TDirectionTracer::EDirectionPositive && 
+               widthDir != TDirectionTracer::EDirectionNegative ) )
+            {
+            pinchWidthDelta = widthDelta; 
+            pinchHeightDelta = heightDelta; 
+            }
+        }
+    else if ( widthDelta <= 0 && heightDelta <= 0 )
+        {
+        TDirectionTracer::TDirection widthDir = 
+            iPinchWidthTracer.Direction();
+        TDirectionTracer::TDirection heightDir = 
+            iPinchHeightTracer.Direction();
+                
+        if ( ( widthDir == TDirectionTracer::EDirectionNegative && 
+               heightDir != TDirectionTracer::EDirectionPositive ) || 
+             ( heightDir == TDirectionTracer::EDirectionNegative && 
+               widthDir != TDirectionTracer::EDirectionPositive ) )
+            {
+            pinchWidthDelta = widthDelta;
+            pinchHeightDelta = heightDelta;
+            }                
+        }
+    else
+        {
+        // Ignore mixed changes.
+        }
+
+    // Pinch values are sent in a slight delay in order to prevent
+    // sending of incorrect values when pointer coordinates are
+    // snapped to horizontal/vertical line.
+    //
+    // Now just store delta values here.
+    
+    iStoredTime = aEventTime;
+    iStoredWidthDelta = pinchWidthDelta;
+    iStoredHeightDelta = pinchHeightDelta;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Ends the recognition of the pinch gesture.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::CompleteMultiRecognizingL(
+        const TTime& aEventTime )
+    {
+    if ( iContinuousFeedback )
+        {
+        StopContinuousFeedback();
+        iContinuousFeedback = EFalse;
+        }
+    // Send Pinch stopped event only if pinch was detected
+    if ( iPinchDetected )
+        {
+        TInt movement = 0;
+        
+        if ( CheckDurationThreshold( aEventTime ) )
+            {
+            movement = 
+                CalculateMovement( 
+                    iStoredWidthDelta, 
+                    iStoredHeightDelta );
+            }
+
+        iStoredWidthDelta = 0;
+        iStoredHeightDelta = 0;
+
+        SendPinchEventL( EAknTouchGestureFwStop, movement );
+        }
+    }
+
+
+// ---------------------------------------------------------------------------
+// Resets the recognizer state.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::Reset()
+    {
+    iPinchDetected = EFalse;
+    iPinchRect.SetRect( 0, 0, 0, 0 );
+    iPinchWidthTracer.Reset();
+    iPinchHeightTracer.Reset();
+    iResetPinchWidthTracer = 0;
+    iResetPinchHeightTracer = 0;
+    iStoredWidthDelta = 0;
+    iStoredHeightDelta = 0;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Sends a pinch gesture event to the observer.
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::SendPinchEventL(
+    TAknTouchGestureFwState aGestureState,
+    TInt aMovementDelta )
+    {
+    if ( aGestureState != EAknTouchGestureFwStop )
+        {
+        TTouchFeedbackType feedbackType(
+            FeedbackType( EAknTouchGestureFwPinch ) );
+    
+        if ( feedbackType & ETouchFeedbackVibra )
+            {
+            StartContinuousFeedback( ETouchContinuousSmooth,
+                KAknTouchGestureFwFeedbackIntensity,
+                KAknTouchGestureFwPinchFeedbackTimeout );
+            iContinuousFeedback = ETrue;
+            }
+        }
+        
+    TAknTouchGestureFwPinchEvent pinch;
+    pinch.SetState( aGestureState );
+    pinch.SetMovement( aMovementDelta );
+    SendGestureEventL( pinch );
+    }
+
+
+// ----------------------------------------------------------------------------
+// Returns pinch initial threshold.
+// ----------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::PinchInitialThreshold() const
+    {
+    return Settings().PinchInitialThreshold();    
+    }
+
+
+// ----------------------------------------------------------------------------
+// Returns pinch movement threshold.
+// ----------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::PinchMovementThreshold() const
+    {
+    return Settings().PinchMovementThreshold();
+    }
+
+
+// ----------------------------------------------------------------------------
+// Returns pinch direction change sensitivity.
+// ----------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::PinchDirectionChangeSensitivity() const
+    {
+    return Settings().PinchDirectionChangeSensitivity();
+    }
+
+
+// ----------------------------------------------------------------------------
+// Returns pinch direction reset sensitivity.
+// ----------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::PinchDirectionResetSensitivity() const
+    {
+    return Settings().PinchDirectionResetSensitivity();
+    }
+
+
+// ----------------------------------------------------------------------------
+// Returns pinch dimension threshold.
+// ----------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::PinchDimensionThreshold() const
+    {
+    return Settings().PinchDimensionThreshold();
+    }
+
+// ----------------------------------------------------------------------------
+// Returns pinch maximum confirmation duration.
+// ----------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::PinchMaximumConfirmationDuration() 
+    const
+    {
+    return Settings().PinchMaximumConfirmationDuration() * 
+           KMicroSecondsInMilliSecond;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Calculates bounding rect so that both aPoint1 and aPoint2 are inside
+// the rectangle.
+// ---------------------------------------------------------------------------
+//
+TRect CAknTouchGestureFwPinchRecognizer::CalculateBoundingRect( 
+        const TPoint& aPoint1, 
+        const TPoint& aPoint2 )
+    {
+    TRect result;
+    result.iTl.iX = Min( aPoint1.iX, aPoint2.iX );
+    result.iTl.iY = Min( aPoint1.iY, aPoint2.iY );
+    result.iBr.iX = Max( aPoint1.iX, aPoint2.iX ) + 1;  
+    result.iBr.iY = Max( aPoint1.iY, aPoint2.iY ) + 1;
+    return result;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Snaps aValue to aReference if they are sufficiently close 
+// (defined by aMargin).
+// ---------------------------------------------------------------------------
+//
+void CAknTouchGestureFwPinchRecognizer::NormalizeValue( 
+        TInt& aValue, 
+        TInt aReference, 
+        TInt aMargin )
+    {
+    if ( Abs( aReference - aValue ) < aMargin )
+        {
+        aValue = aReference;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Calculates movement value 
+// ---------------------------------------------------------------------------
+//
+TInt CAknTouchGestureFwPinchRecognizer::CalculateMovement( 
+        TInt aWidthDelta, 
+        TInt aHeightDelta )
+    {
+    TInt result = 0;
+    
+    if ( aWidthDelta >= 0 && aHeightDelta >= 0 )
+        {
+        result = Max( aWidthDelta, aHeightDelta );
+        }
+    else if ( aWidthDelta <= 0 && aHeightDelta <= 0 )
+        {
+        result = Min( aWidthDelta, aHeightDelta );
+        }
+    else
+        {
+        // Mixed case, return 'no movement'.
+        }
+
+    return result;
+    }
+
+// ---------------------------------------------------------------------------
+// Checks duration threshold. 
+// ---------------------------------------------------------------------------
+//
+TBool CAknTouchGestureFwPinchRecognizer::CheckDurationThreshold( 
+        const TTime& aEventTime ) const
+    {
+    TTimeIntervalMicroSeconds duration = 
+        aEventTime.MicroSecondsFrom( iStoredTime );
+    TTimeIntervalMicroSeconds durationThreshold( 
+        PinchMaximumConfirmationDuration() );
+    
+    return ( duration < durationThreshold );
+    }
+
+// End of File