diff -r 000000000000 -r 2f259fa3e83a akntouchgesturefw/src/akntouchgesturefwpinchrecognizer.cpp --- /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