/** Copyright (c) 2008-2008 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: Implementation of member functions of * AlfExAnalogDialerControl.**/#include <coemain.h>// Visuals#include <alf/alfgridlayout.h>#include <alf/alftextvisual.h>#include <alf/alfimagevisual.h>#include <alf/alflinevisual.h>// Layouts#include <alf/alfdecklayout.h>#include <alf/alfcurvepath.h>#include <alf/alfcurvepathlayout.h>// Brushes#include <alf/alfborderbrush.h>#include <alf/alfbrusharray.h>#include <alf/alfimagebrush.h>#include <alf/alfimage.h>#include <alf/alfshadowborderbrush.h>#include <alf/alfgradientbrush.h>// Unsorted#include <aknutils.h>#include <alf/alftransformation.h>#include <alf/alfevent.h>#include <alf/alfdisplay.h>#include <alf/alfenv.h>#include <alf/alfmappingfunctions.h>#include <alf/alfroster.h>#include <e32math.h>#include "alfexanalogdialercontrol.h"#include "alfexanalogdialerfeedback.h"// Image names_LIT(KImageAnalogDialerBackground, "background.png");_LIT(KImageAnalogDialerNumbers, "numbers.png");_LIT(KImageAnalogDialerPlate, "plate.png");_LIT(KImageAnalogDialerStopper, "stopper.png");#define RADTODEG(Rad) ((180.0 * Rad) / KPi)// dimension of gridconst TInt KDimensionsOfGrid = 2;// Tags for visuals which have always same appearance. // TTagAnalogDialerVisual is used for indexingconst char* KTagArrayTheRest[] = { "main", "display", "phone", "plate-deck", "plate-image", "stopper" }; // Dimensions of grid. iLayoutMode is used for indexingconst TInt KRowsAndColumnsOfGrid[][KDimensionsOfGrid] = { { 1, 2 }, // columns, rows { 2, 1 } };// Templates for displaying number_LIT(KNumberDisplayTemplate, "%d");// Radius of Clear area of plate. Per cents from radius of plate.const TInt KPlateClearRadius = 25;// Unit speed of returning plate to idle positionconst TInt KPlateReturnSpeed = 65;// As dragging events occur very often, redrawing plate is optimised that// it is not drawn after every dragging event. Change of KPlateDraggingDelta // degrees is needed.const TInt KPlateDraggingDelta = 2;// position of stopper in degrees. Angle is 43const TInt KStopperAngle = 360-43;// Angle of first number// Numbers are on angle between KPlateNumbersStartAngle and (KPlateNumbersStartAngle + KPlateNumbersDistributionAngle)const TInt KPlateNumbersStartAngle = 3;// angle of arch on which numbers (or holes of plate) are distributed// Numbers are on angle between KPlateNumbersStartAngle and (KPlateNumbersStartAngle + KPlateNumbersDistributionAngle)const TInt KPlateNumbersDistributionAngle = 290;// minimum rotation of plate that number becomes selectedconst TInt KPlateMinimumRotateAngle=43;// font color const TRgb KFontColor = TRgb(192,192,192);// Size of padding of the root grid layout//const TReal32 KPaddingPercentage = .05;// ---------------------------------------------------------------------------// NewL// ---------------------------------------------------------------------------//CAlfExAnalogDialerControl* CAlfExAnalogDialerControl::NewL(CAlfEnv& aEnv) { CAlfExAnalogDialerControl* self = CAlfExAnalogDialerControl::NewLC(aEnv); CleanupStack::Pop(self); return self; }// ---------------------------------------------------------------------------// NewLC// ---------------------------------------------------------------------------//CAlfExAnalogDialerControl* CAlfExAnalogDialerControl::NewLC(CAlfEnv& aEnv) { CAlfExAnalogDialerControl* self = new (ELeave) CAlfExAnalogDialerControl(); CleanupStack::PushL(self); self->ConstructL(aEnv); return self; }// ---------------------------------------------------------------------------// ~CAlfExAnalogDialerControl// ---------------------------------------------------------------------------//CAlfExAnalogDialerControl::~CAlfExAnalogDialerControl() { if (iFlags & EAnalogDialerControlFlagLongTapRegistered) { Display()->Roster().RemovePointerEventObserver(EAlfPointerEventReportLongTap, *this); } Env().CancelCommands(this); delete iFeedback; }// ---------------------------------------------------------------------------// CAlfExAnalogDialerControl// ---------------------------------------------------------------------------//CAlfExAnalogDialerControl::CAlfExAnalogDialerControl() : CAlfControl() { }// ---------------------------------------------------------------------------// ConstructL// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::ConstructL(CAlfEnv& aEnv) { CAlfControl::ConstructL(aEnv); // Id cannot be be set in constructor SetId(KAlfExAnalogDialerControlId); // resolve layout: portrait or landscape iLayoutMode = ResolveLayout(iSquareSide, iLongerSide); (iLayoutMode == ELayoutAnalogDialerPortrait) ? SetupPortraitModeL() : SetupLandscapeModeL(); // set up vibra system iFeedback = CAlfExAnalogDialerFeedback::NewL(); } // ---------------------------------------------------------------------------// Creates layouts and visual for portrait layout// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::SetupPortraitModeL() { // resolve layout: portrait or landscape AddGridLayoutL(); // from top to bottom 1) display pane 2) dialer AddDisplayPaneL(); AddDialerL(); //AddEffectLayout(); }// ---------------------------------------------------------------------------// Creates layouts and visual for portrait layout// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::SetupLandscapeModeL() { // resolve layout: portrait or landscape AddGridLayoutL(); // from left to right 1) dialer 2) display pane AddDialerL(); AddDisplayPaneL(); //AddEffectLayout(); }// ---------------------------------------------------------------------------// Creates grid layout and returns it// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::AddGridLayoutL() { // Create a grid layout to act as the root layout visual. Not owned if (!iRootLayout) { iRootLayout = CAlfGridLayout::AddNewL(*this, KRowsAndColumnsOfGrid[iLayoutMode][EColumn], KRowsAndColumnsOfGrid[iLayoutMode][ERow]); iRootLayout->SetTagL(_L8(KTagArrayTheRest[ETagAnalogDialerMain])); // Brushed must enabled before they can be used iRootLayout->EnableBrushesL(); // Background image is implemented using image brush CAlfTexture& backgroundTexture = Env().TextureManager().LoadTextureL( KImageAnalogDialerBackground, EAlfTextureFlagDefault, KAlfAutoGeneratedTextureId); // Default scale mode CAlfImageVisual::EScaleFit is OK CAlfImageBrush* brush = CAlfImageBrush::NewLC(Env(), TAlfImage(backgroundTexture)); // sets background image behind the visual brush->SetLayer(EAlfBrushLayerBackground); iRootLayout->Brushes()->AppendL(brush, EAlfHasOwnership); CleanupStack::Pop(brush); } else { // Reset current dimensions // There is only one dimension for(TInt i=0; i<KDimensionsOfGrid; i++) { // resetting non existing weights causes Leave TRAP_IGNORE(iRootLayout->RemoveWeightL(EAlfGridColumn, i)); TRAP_IGNORE(iRootLayout->RemoveWeightL(EAlfGridRow, i)); } // now set new dimension iRootLayout->SetColumnsL(KRowsAndColumnsOfGrid[iLayoutMode][EColumn]); iRootLayout->SetRowsL(KRowsAndColumnsOfGrid[iLayoutMode][ERow]); } // Sets weights of grid SetWeightsOfGridL(*iRootLayout, iLayoutMode, iSquareSide, iLongerSide, iPlateTopOffset); // Set the padding of the root layout // TODO: Padding disabled for now. This seems to mess the angle calculations as we the calculations do not consider // the effect of padding. /* TRect displayRect = Env().PrimaryDisplay().VisibleArea(); TInt pad = displayRect.Height(); if ( iLayoutMode == ELayoutAnalogDialerLandscape ) { pad = displayRect.Width(); } layout->SetInnerPadding(TPoint( pad * KPaddingExtents[iLayoutMode][EColumn], pad * KPaddingExtents[iLayoutMode][ERow])); layout->SetPadding(pad * KPaddingPercentage);*/ }// ---------------------------------------------------------------------------// Handles pointer down events// ---------------------------------------------------------------------------//TBool CAlfExAnalogDialerControl::HandlePointerDownEventL(const TAlfEvent& aEvent) { CAlfVisual* onVisual = aEvent.Visual(); // Dragging may start only on top of image if (onVisual == iStopper) { // Is plate rotating starting? if (IsPointerEventFollowingNumbersArcPath(aEvent)) { // Plate rotation is starting // Register to receive dragging events Display()->Roster().AddPointerEventObserver(EAlfPointerEventReportDrag, *this); ResetDraggingData(); // Start dragging iFlags |= EAnalogDialerControlDragging; } // Is clear key pressed? else if (IsPointerEventOnClearKey(aEvent)) { ResetClearKeyData(); // Register for long tap events if (!(iFlags & EAnalogDialerControlFlagLongTapRegistered)) { // Clear key handling needs long tap events Display()->Roster().AddPointerEventObserver(EAlfPointerEventReportLongTap, *this); iFlags |= EAnalogDialerControlFlagLongTapRegistered; } // Start clear key event iFlags |= EAnalogDialerControlClearKeyEvent; } } return ETrue; }// ---------------------------------------------------------------------------// Handles events while dragging// ---------------------------------------------------------------------------//TBool CAlfExAnalogDialerControl::HandleDraggingEventL(const TAlfEvent& aEvent) { if (iFlags & EAnalogDialerControlDragging) { TPoint plateCentricPoint; ConvertEventToPlateCentricPosition(aEvent, iPlateTopOffset, iSquareSide, plateCentricPoint); TInt plateRadius = iSquareSide / 2; if (!IsPointerEventOnArcPath(plateCentricPoint, plateRadius, plateRadius * KPlateClearRadius / 100)) { FinishDragging(); } else { // Pointer event is following path // Get angle of pointer event and do further calculation based on it TInt degrees = Angle(plateCentricPoint.iX, plateCentricPoint.iY); // Save the angle of first dragging pointer event. // It is used to resolve selected number. if (iDraggingData.iInitialAngle == 0) { iDraggingData.iInitialAngle = degrees; iDraggingData.iPreviousAngle = degrees; } // As dragging events occur very often, redrawing plate is optimised that // it is not drawn after every dragging event. Change of KPlateDraggingDelta degrees // is needed until plate is redrawn. if ( degrees >= iDraggingData.iPreviousAngle + KPlateDraggingDelta || degrees <= iDraggingData.iPreviousAngle - KPlateDraggingDelta) { ResolveXAxisCrossing(degrees); iDraggingData.iPreviousAngle = degrees; if ( iDraggingData.iIsClockWiseOverZeroReached && degrees < KStopperAngle) { if (iDraggingData.iStopperReached) { // do not redraw as plate cannot rotate more return ETrue; } // stopper has been reached iDraggingData.iStopperReached = ETrue; } else if ( !iDraggingData.iIsClockWiseOverZeroReached && degrees > iDraggingData.iInitialAngle) { // Rotation counter clockwise to the left of idle position is asked // Plate does not rotate counterclockwise to the left of idle position return ETrue; } else if (iDraggingData.iStopperReached) { // user has dragged 360 degrees and and is dragging now between idle and stopper position return ETrue; } else { // rotating when angle is between idle and stopper position iDraggingData.iStopperReached = EFalse; } //DebugDraggingData(degrees); RotatePlate( *iImage, iDraggingData.iInitialAngle, degrees, iDraggingData.iIsClockWiseOverZeroReached); } // else - delta of rotation has not reached KPlateDraggingDelta } } return ETrue; }// ---------------------------------------------------------------------------// Handle pointer up event// ---------------------------------------------------------------------------//TBool CAlfExAnalogDialerControl::HandlePointerUpEventL(const TAlfEvent& aEvent) { if (iFlags & EAnalogDialerControlDragging) { FinishDragging(); } else if (iFlags & EAnalogDialerControlClearKeyEvent) { if (IsPointerEventOnClearKey(aEvent)) { ClearNumberDisplay(iFlags&EAnalogDialerControlFlagLongTapPressed); } // else - lifting pointer outside Clear key area cancels clearing number ResetClearKeyData(); } return ETrue; }// ---------------------------------------------------------------------------// Handle long tap event// ---------------------------------------------------------------------------//TBool CAlfExAnalogDialerControl::HandlePointerLongTapEventL(const TAlfEvent& /*aEvent*/) { if (iFlags & EAnalogDialerControlClearKeyEvent) { iFlags |= EAnalogDialerControlFlagLongTapPressed; } return ETrue; }// ---------------------------------------------------------------------------// Events from framework// ---------------------------------------------------------------------------//TBool CAlfExAnalogDialerControl::OfferEventL(const TAlfEvent& aEvent) { if (aEvent.IsPointerEvent() && aEvent.PointerDown()) { return HandlePointerDownEventL(aEvent); } else if(aEvent.IsPointerEvent() && aEvent.PointerEvent().iType == TPointerEvent::EDrag ) { return HandleDraggingEventL(aEvent); } else if(aEvent.IsPointerEvent() && aEvent.PointerUp()) { return HandlePointerUpEventL(aEvent); } else if(aEvent.IsPointerEvent() && aEvent.PointerLongTap()) { return HandlePointerLongTapEventL(aEvent); } return EFalse; }// ---------------------------------------------------------------------------// AddNumberToDisplayPaneL// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::AddNumberToDisplayPaneL(TInt aNumber) { TBuf<8> number; // Second parameter: number cannot be 10 number.Format(KNumberDisplayTemplate, (aNumber+1)%10); if (iDisplayNumber.Length() + number.Length() <= KNumberDisplayBufferLength) { iDisplayNumber.Append(number); iDisplayPane->SetTextL(iDisplayNumber); } }// ---------------------------------------------------------------------------// Adds content into display pane of the application// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::AddDisplayPaneL() { CAlfDeckLayout* displayDeck = CAlfDeckLayout::AddNewL(*this, iRootLayout); // Add TextVisual for displaying text iDisplayPane = CAlfTextVisual::AddNewL(*this, displayDeck); iDisplayPane->SetTagL(_L8( KTagArrayTheRest[ETagAnalogDialerDisplay])); iDisplayPane->EnableBrushesL(); iDisplayPane->SetAlign(EAlfAlignHRight, EAlfAlignVCenter); iDisplayPane->SetFlag(EAlfVisualFlagManualLayout); // Decoration if (iLayoutMode == ELayoutAnalogDialerPortrait) { iDisplayPane->SetCenteredPosAndSize(TPoint(180,100),TSize(300,50)); } else { iDisplayPane->SetCenteredPosAndSize(TPoint(150,180),TSize(200,300)); } iDisplayPane->SetStyle(EAlfTextStyleTitle); iDisplayPane->SetColor(KFontColor); // Display rectangular CAlfShadowBorderBrush* brush = CAlfShadowBorderBrush::NewLC(Env(),6); brush->SetOpacity(.25f); // Attach brush and transfer its ownership to the visual. iDisplayPane->Brushes()->AppendL(brush, EAlfHasOwnership); CleanupStack::Pop(brush); }// ---------------------------------------------------------------------------// Adds content into dialer pane of the application// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::AddDialerL() { // Deck CAlfDeckLayout* dialerDeck = CAlfDeckLayout::AddNewL(*this, iRootLayout); dialerDeck->SetTagL(_L8(KTagArrayTheRest[ETagAnalogDialerDialer])); // TODO: for testing dialerDeck->EnableTransformationL(); dialerDeck->EnableBrushesL(); // load textures // the dialer consists of 3 pictures which are put on top of each other. // Bottom most is the image with the numbers CAlfTexture& dialerBgTexture = Env().TextureManager().LoadTextureL( KImageAnalogDialerNumbers, EAlfTextureFlagDefault, KAlfAutoGeneratedTextureId); // Default scale mode CAlfImageVisual::EScaleFit is OK CAlfImageVisual* image2 = CAlfImageVisual::AddNewL(*this,dialerDeck); // attach texture to image visual image2->SetImage(TAlfImage(dialerBgTexture)); // on top of the numbers is the plate picture, which will be rotated CAlfTexture& circle = Env().TextureManager().LoadTextureL( KImageAnalogDialerPlate, EAlfTextureFlagDefault, KAlfAutoGeneratedTextureId); // Default scale mode CAlfImageVisual::EScaleFit is OK iImage = CAlfImageVisual::AddNewL(*this,dialerDeck); iImage->SetImage(TAlfImage(circle)); // on top the rotating plate is image with the stopper and shadows CAlfTexture& stopper = Env().TextureManager().LoadTextureL( KImageAnalogDialerStopper, EAlfTextureFlagDefault, KAlfAutoGeneratedTextureId); // Default scale mode CAlfImageVisual::EScaleFit is OK iStopper = CAlfImageVisual::AddNewL(*this,dialerDeck); iStopper->SetImage(TAlfImage(stopper)); }// ---------------------------------------------------------------------------// SwitchLayoutL// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::SwitchLayoutL() { iLayoutMode = ResolveLayout(iSquareSide, iLongerSide); iLayoutMode == ELayoutAnalogDialerPortrait ? SetupPortraitModeL() : SetupLandscapeModeL(); if (iLayoutMode == ELayoutAnalogDialerPortrait) { MoveVisualToFirst(_L8(KTagArrayTheRest[ETagAnalogDialerDisplay])); } else { MoveVisualToFirst(_L8(KTagArrayTheRest[ETagAnalogDialerDialer])); } iFeedback->Start(); // expose new root layout TAlfTimedValue opacity(0.0f); opacity.SetTarget(255, 500); iRootLayout->SetOpacity(opacity); }// ---------------------------------------------------------------------------// Moves visual with given tag to first visual in the layout// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::MoveVisualToFirst(const TDesC8& aTag) { CAlfVisual* layout = FindTag(aTag); if (layout) { // layout to move, index in the layout, transition time iRootLayout->Reorder(*layout, 0, 0); } }// ---------------------------------------------------------------------------// Hides the visual// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::PrepareForLayoutSwitchL() { CAlfVisual* layout = FindTag(_L8(KTagArrayTheRest[ETagAnalogDialerMain])); if ( layout ) { TAlfTimedValue opacity(1.0f); opacity.SetTarget( 0.0f, 500); layout->SetOpacity(opacity); } iFeedback->Start(); User::After(500000); }// ---------------------------------------------------------------------------// Resolves current layout.// ---------------------------------------------------------------------------// CAlfExAnalogDialerControl::TLayoutMode CAlfExAnalogDialerControl::ResolveLayout( TInt& aShorterSide, TInt& aLongerSide) const { TLayoutMode layoutMode = ELayoutAnalogDialerPortrait; TRect displayRect = Env().PrimaryDisplay().VisibleArea(); TInt height = displayRect.Height(); TInt width = displayRect.Width(); if (width > height) { layoutMode = ELayoutAnalogDialerLandscape; aShorterSide = height; aLongerSide = width; } else { // default mode OK aShorterSide = width; aLongerSide = height; } return layoutMode; }// ---------------------------------------------------------------------------// ResolveXAxisCrossing// ---------------------------------------------------------------------------//void CAlfExAnalogDialerControl::ResolveXAxisCrossing(TInt aAngle) { // Delta of angle change, which indicates X-axis crossing const TInt KXAxisCrossingAngleDelta = 300; TInt degreesDelta = aAngle - iDraggingData.iPreviousAngle; // switch from 0 to 359? if (degreesDelta > KXAxisCrossingAngleDelta) { iDraggingData.iIsClockWiseOverZeroReached = ETrue; } // switch from 359 to 0? else if ( iDraggingData.iIsClockWiseOverZeroReached && degreesDelta < (-KXAxisCrossingAngleDelta)) { iDraggingData.iIsClockWiseOverZeroReached = EFalse; } }// ---------------------------------------------------------------------------// Calculate angle of triangle, when adjacent and opposite sides are known// ---------------------------------------------------------------------------// TInt CAlfExAnalogDialerControl::Angle(TReal32 aAdjacent, TReal32 aOpposite) { if (aAdjacent == 0) { aAdjacent = 0.01; } if (aOpposite == 0) { aOpposite = 0.01; } TReal angle; Math::ATan(angle, Abs(aOpposite / aAdjacent)); if (aAdjacent < 0 && aOpposite > 0) { angle = KPi - angle; } else if (aAdjacent < 0 && aOpposite < 0) { angle = KPi + angle; } else if (aAdjacent > 0 && aOpposite < 0) { angle = 2* KPi - angle; } return RADTODEG(angle); }// ---------------------------------------------------------------------------// Calculate hypotenuse of triangle when length of adjacent and opposite sides are known// ---------------------------------------------------------------------------// TInt CAlfExAnalogDialerControl::Hypotenuse(TInt aA, TInt aB) { aA *= aA; aB *= aB; TReal hypotenuse; Math::Sqrt(hypotenuse, aA + aB); return hypotenuse; }// ---------------------------------------------------------------------------// Layout of portrait and landscape modes are hardwired into implementation. // ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::SetWeightsOfGridL( CAlfGridLayout& aGridLayout, TLayoutMode aLayout, TInt aSquareSide, TInt aLongerSide, TInt& aPlateTop) { // Set up a variable for default value. RArray<TInt> weightsDefault; User::LeaveIfError(weightsDefault.Append(1)); // sets weights of grid. Use pixels to get exact adjustment of pane sizes RArray<TInt> weights; if (aLayout == ELayoutAnalogDialerPortrait) { // top: display User::LeaveIfError(weights.Append(aLongerSide-aSquareSide)); // bottom: dialer User::LeaveIfError(weights.Append(aSquareSide)); aGridLayout.SetRowsL(weights); aGridLayout.SetColumnsL(weightsDefault); aPlateTop = aLongerSide-aSquareSide; } else { // left: dialer User::LeaveIfError(weights.Append(aSquareSide)); // right: display User::LeaveIfError(weights.Append(aLongerSide-aSquareSide)); aGridLayout.SetRowsL(weightsDefault); aGridLayout.SetColumnsL(weights); aPlateTop = 0; } weights.Close(); weightsDefault.Close(); }// ---------------------------------------------------------------------------// ConvertEventToPlateCentricPosition// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::ConvertEventToPlateCentricPosition( const TAlfEvent& aEvent, TInt aPlateTopOffset, TInt aSquareSide, TPoint& aPlateCentricPoint) { // event position is converted in a such way that centre position of the plate is (0,0) // iY: value is negative, if position is above the centre position // iY: value is position, if position is under the centre position // iX: value is negative, if position is to the left the centre position // iX: value is position, if position is to the right of the centre position const TInt plateCentreY = aPlateTopOffset + aSquareSide / 2; aPlateCentricPoint.iX = aEvent.PointerEvent().iParentPosition.iX - aSquareSide / 2; aPlateCentricPoint.iY = plateCentreY - aEvent.PointerEvent().iParentPosition.iY ; }// ---------------------------------------------------------------------------// Checks whether pointer event occurred between outer and inner radius// ---------------------------------------------------------------------------// TBool CAlfExAnalogDialerControl::IsPointerEventOnArcPath( TPoint& aPlateCentricPoint, TInt aOuterRadius, TInt aInnerRadius) { // Checks whether position is along a path, which is between two imaginary circles. // Radius of circles are given. TBool ret = ETrue; TInt distanceFromPlateCentre = Hypotenuse(aPlateCentricPoint.iX, aPlateCentricPoint.iY); if (distanceFromPlateCentre > aOuterRadius || distanceFromPlateCentre < aInnerRadius) { ret = EFalse; } return ret; }// ---------------------------------------------------------------------------// Dragging has been stopped. Resets dragging data// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::ResetDraggingData() { iFlags &= (~EAnalogDialerControlDragging); iDraggingData.iInitialAngle = 0; iDraggingData.iPreviousAngle = 0; iDraggingData.iIsClockWiseOverZeroReached = EFalse; iDraggingData.iStopperReached=EFalse; }// ---------------------------------------------------------------------------// Dragging has been stopped. Resets dragging data// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::ResetClearKeyData() { iFlags &= (~EAnalogDialerControlClearKeyEvent); iFlags &= (~EAnalogDialerControlFlagLongTapPressed); }// ---------------------------------------------------------------------------// Turns plate image back to idle position// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::RotatePlateToIdlePosition(CAlfImageVisual& aImage) { // Rotate the image. This has immediate effect. TAlfTimedValue tValue; tValue.SetTargetWithSpeed(0, // position zero KPlateReturnSpeed); aImage.SetTurnAngle(tValue); }// ---------------------------------------------------------------------------// Rotates image// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::RotatePlate( CAlfImageVisual& aImage, TInt aInitialAngle, TInt aTargetAngle, TBool aIsClockWiseOverZeroReached) { // When rotates clockwise, rotation angle is negative TInt rotateDelta = aTargetAngle-aInitialAngle; if (aIsClockWiseOverZeroReached) { // Angle is big value (near to 360), which cannot be used as such // Convert it to negative value rotateDelta = (aTargetAngle-360)-aInitialAngle; } //RDebug::Printf("***AnalogDialer rotateDelta : %d",rotateDelta); TAlfTimedValue tValue(rotateDelta); aImage.SetTurnAngle(tValue); }// ---------------------------------------------------------------------------// Debugs dragging data + given angle // ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::DebugDraggingData(TInt aAngle) { RDebug::Printf("\n***AnalogDialer:"); RDebug::Printf("***AnalogDialer iFlags & EAnalogDialerControlDragging: %d",iFlags & EAnalogDialerControlDragging); RDebug::Printf("***AnalogDialer iDraggingData.iInitialAngle: %d",iDraggingData.iInitialAngle); RDebug::Printf("***AnalogDialer iDraggingData.iPreviousAngle: %d",iDraggingData.iPreviousAngle); RDebug::Printf("***AnalogDialer iDraggingData.iIsClockWiseOverZeroReached: %d",iDraggingData.iIsClockWiseOverZeroReached); RDebug::Printf("***AnalogDialer iDraggingData.iStopperReached: %d",iDraggingData.iStopperReached); RDebug::Printf("***AnalogDialer aAngle: %d",aAngle); RDebug::Printf("\n"); }// ---------------------------------------------------------------------------// Returns selected number based on rotated angle of plate // ---------------------------------------------------------------------------// TInt CAlfExAnalogDialerControl::DialedNumber( TInt aInitialAngle, TInt aCurrentAngle) const { // Default value when dragging event has not crossed zero-angle X-axis TInt deltaAngle = aCurrentAngle-aInitialAngle; // aCurrentAngle must be an angle derived directly from ConvertEventToPlateCentricPosition if (iDraggingData.iIsClockWiseOverZeroReached) { deltaAngle = aInitialAngle+(360-KStopperAngle); } // subtract required minimun angle deltaAngle = Abs(deltaAngle)-KPlateMinimumRotateAngle; if (deltaAngle<0) { return KErrNotFound; } // TODO. These values could be made constant to use plate with other number systems as decimal // e.g. binary, or hexadecimal // Finally calculate dialed number based on rotated angle TInt angleOfANumber(KPlateNumbersDistributionAngle/10); TInt dialedNumber = deltaAngle/angleOfANumber; if (dialedNumber > 9) { dialedNumber = 9; } return dialedNumber; }// ---------------------------------------------------------------------------// Checks whether pointer is on path allowed for plate rotation// ---------------------------------------------------------------------------// TBool CAlfExAnalogDialerControl::IsPointerEventFollowingNumbersArcPath( const TAlfEvent& aEvent ) const { TPoint plateCentricPoint; ConvertEventToPlateCentricPosition(aEvent, iPlateTopOffset, iSquareSide, plateCentricPoint); TInt degrees = Angle( plateCentricPoint.iX, plateCentricPoint.iY); // Third parameter: Radius of clear area belongs to clear area TInt plateRadius = iSquareSide / 2; if ( IsPointerEventOnArcPath( plateCentricPoint, plateRadius, (plateRadius*KPlateClearRadius/100)+1)) { return ETrue; } return EFalse; }// ---------------------------------------------------------------------------// Checks whether pointer is on area of clear key// ---------------------------------------------------------------------------// TBool CAlfExAnalogDialerControl::IsPointerEventOnClearKey( const TAlfEvent& aEvent ) const { TPoint plateCentricPoint; ConvertEventToPlateCentricPosition(aEvent, iPlateTopOffset, iSquareSide, plateCentricPoint); TInt plateRadius = iSquareSide / 2; if ( IsPointerEventOnArcPath( plateCentricPoint, plateRadius*KPlateClearRadius/100, 0)) { return ETrue; } return EFalse; }// ---------------------------------------------------------------------------// Performs actions needed when dragging is finished// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::FinishDragging() { // Dragging has finished // Remove pointer dragging observer, which will gain us performance Display()->Roster().RemovePointerEventObserver( EAlfPointerEventReportDrag,*this ); DialedNumberToDisplay(iDraggingData.iInitialAngle, iDraggingData.iPreviousAngle); RotatePlateToIdlePosition(*iImage); ResetDraggingData(); }// ---------------------------------------------------------------------------// Puts dialed number into number display// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::DialedNumberToDisplay( TInt aInitialAngle, TInt aCurrentAngle ) { TInt dialedNumber = DialedNumber( aInitialAngle, aCurrentAngle ); // Filter KErrNotFound out if (dialedNumber>=0) { TRAP_IGNORE( AddNumberToDisplayPaneL( dialedNumber ) ); } }// ---------------------------------------------------------------------------// Clear number display either wholly or last number// ---------------------------------------------------------------------------// void CAlfExAnalogDialerControl::ClearNumberDisplay(TBool aClearWholeDisplay) { TInt lengthNumber = iDisplayNumber.Length(); if (lengthNumber == 0) { return; } if (aClearWholeDisplay) { TBuf<1> clearBuf; iDisplayNumber.Copy(clearBuf); } else { // delete one number iDisplayNumber.Delete(lengthNumber-1,1); } TRAP_IGNORE( iDisplayPane->SetTextL(iDisplayNumber) ); }void CAlfExAnalogDialerControl::AddEffectLayout() { }// End of File