--- /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