diff -r 000000000000 -r 15bf7259bb7c exampleapps/alfexanalogdialer/src/alfexanalogdialercontrol.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exampleapps/alfexanalogdialer/src/alfexanalogdialercontrol.cpp Tue Feb 02 07:56:43 2010 +0200 @@ -0,0 +1,1081 @@ +/* +* 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 + +// Visuals +#include +#include +#include +#include + +// Layouts +#include +#include +#include + +// Brushes +#include +#include +#include +#include +#include +#include + +// Unsorted +#include +#include +#include +#include +#include +#include +#include + +#include + +#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) + +// font color +const TRgb KFontColor = TRgb(192,192,192); + +// dimension of grid +const TInt KDimensionsOfGrid = 2; + +// Tags for visuals which have always same appearance. +// TTagAnalogDialerVisual is used for indexing +const char* KTagArray[] = + { + "root-layout", + "display-deck", + "display-visual", + "plate-deck", + "numbers-visual", + "plate-visaul", + "stopper-visual" + }; + +// Dimensions of grid. iLayoutMode is used for indexing +const TInt KRowsAndColumnsOfGrid[][KDimensionsOfGrid] = + { + { 1, 2 }, // columns, rows + { 2, 1 } + }; + +// Template 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 position. Degrees per second. +const TInt KPlateReturnSpeed = 65; + +// As dragging events occur very often, redrawing plate is optimised that +// it is not drawn after every dragging event. +// Plate is redrawn when angle of the plate has changed KPlateDraggingDelta clockwise. +const TInt KPlateDraggingDelta = 2; + +// position of stopper in degrees. Angle is 43 clockwise down from the X-axis +const TInt KStopperAngle = 360-43; + +// angle of arch on which numbers (or holes) are distributed +const TInt KPlateNumbersDistributionAngle = 290; + +// minimum rotation of plate that a number becomes selected. If plate has been dialed +// less than or equal to KPlateMinimumRotateAngle degrees, number is not selected. +const TInt KPlateMinimumRotateAngle=43; + +// --------------------------------------------------------------------------- +// 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() + { + // Remove observer is still there + 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. Id is needed in appui to retrieve control from control group + SetId(KAlfExAnalogDialerControlId); + + // resolve layout: portrait or landscape + iLayoutMode = ResolveLayout(iSquareSide, iLongerSide); + + // setup visuals accroding to layout + (iLayoutMode == ELayoutAnalogDialerPortrait) ? SetupPortraitModeL() : SetupLandscapeModeL(); + + // set up vibra system + iFeedback = CAlfExAnalogDialerFeedback::NewL(); + } + + + +// --------------------------------------------------------------------------- +// Creates layouts and visual for portrait layout +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::SetupPortraitModeL() + { + // Setup grid (root layout) + AddGridLayoutL(); + + // from top to bottom 1) display pane 2) dialer + AddDisplayPaneL(); + AddDialerL(); + + //AddEffectLayout(); + } + +// --------------------------------------------------------------------------- +// Creates layouts and visual for portrait layout +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::SetupLandscapeModeL() + { + // Setup grid (root layout) + AddGridLayoutL(); + + // from left to right 1) dialer 2) display pane + AddDialerL(); + AddDisplayPaneL(); + + //AddEffectLayout(); + } + + +// --------------------------------------------------------------------------- +// Creates grid layout or modify existing one after layout change +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::AddGridLayoutL() + { + if (!iRootLayout) + { + // Fresh start of the application. Create a grid layout to act as the root layout visual. + iRootLayout = CAlfGridLayout::AddNewL(*this, + KRowsAndColumnsOfGrid[iLayoutMode][EAlfGridColumn], + KRowsAndColumnsOfGrid[iLayoutMode][EAlfGridRow]); + // Tag is not really needed, because root layout assigned to member variable + iRootLayout->SetTagL(_L8(KTagArray[ETagAnalogDialerRootLayout])); + + // 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); + + // Append brush to the layout and transfer its ownership to the visual. + iRootLayout->Brushes()->AppendL(brush, EAlfHasOwnership); + CleanupStack::Pop(brush); + } + else + { + // Layout change has taken place. Modify dimensions and weights + // Reset current dimension + for(TInt i=0; iRemoveWeightL(EAlfGridColumn, i)); + TRAP_IGNORE(iRootLayout->RemoveWeightL(EAlfGridRow, i)); + } + // now set up new dimension + iRootLayout->SetColumnsL(KRowsAndColumnsOfGrid[iLayoutMode][EAlfGridColumn]); + iRootLayout->SetRowsL(KRowsAndColumnsOfGrid[iLayoutMode][EAlfGridRow]); + } + + // 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][EAlfGridColumn], + pad * KPaddingExtents[iLayoutMode][EAlfGridRow])); + layout->SetPadding(pad * KPaddingPercentage);*/ + } + + +// --------------------------------------------------------------------------- +// Adds content into display pane of the application +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::AddDisplayPaneL() + { + CAlfDeckLayout* displayDeck = CAlfDeckLayout::AddNewL(*this, iRootLayout); + displayDeck->SetTagL(_L8( KTagArray[ETagAnalogDialerDisplayDeck])); + + // Add TextVisual number display + iDisplayPane = CAlfTextVisual::AddNewL(*this, displayDeck ); + // Tag is not really needed + iDisplayPane->SetTagL(_L8( KTagArray[ETagAnalogDialerDisplayVisual])); + 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); + + + // Enable brushed for borders etc + iDisplayPane->EnableBrushesL(); + + // Display rectangular + CAlfShadowBorderBrush* brush = CAlfShadowBorderBrush::NewLC(Env(),6); + brush->SetOpacity(.25f); + + // Append brush to the visual 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() + { + // Dialer consists of three images on top of each other. Deck is needed to manage them + // Parent layout is grid + CAlfDeckLayout* dialerDeck = CAlfDeckLayout::AddNewL(*this, iRootLayout); + dialerDeck->SetTagL(_L8(KTagArray[ETagAnalogDialerPlateDeck])); + + // Load images: + // 1) Image with the numbers + CAlfTexture& numbersTexture = Env().TextureManager().LoadTextureL( KImageAnalogDialerNumbers, + EAlfTextureFlagDefault, + KAlfAutoGeneratedTextureId); + + // Parent layout is deck + CAlfImageVisual* numbers = CAlfImageVisual::AddNewL(*this,dialerDeck); + // Default scale mode CAlfImageVisual::EScaleFit is OK + // attach texture to image visual + numbers->SetImage(TAlfImage(numbersTexture)); + + // 2) plate with holes + CAlfTexture& plateTexture = Env().TextureManager().LoadTextureL( KImageAnalogDialerPlate, + EAlfTextureFlagDefault, + KAlfAutoGeneratedTextureId); + // Parent layout is deck + iPlate = CAlfImageVisual::AddNewL(*this,dialerDeck); + // Default scale mode CAlfImageVisual::EScaleFit is OK + iPlate->SetImage(TAlfImage(plateTexture)); + + // 2) stopper + CAlfTexture& stopper = Env().TextureManager().LoadTextureL( KImageAnalogDialerStopper, + EAlfTextureFlagDefault, + KAlfAutoGeneratedTextureId); + // Parent layout is deck + iStopper = CAlfImageVisual::AddNewL(*this,dialerDeck); + // Default scale mode CAlfImageVisual::EScaleFit is OK + iStopper->SetImage(TAlfImage(stopper)); + + } + +// --------------------------------------------------------------------------- +// Events from framework +// --------------------------------------------------------------------------- +// +TBool CAlfExAnalogDialerControl::OfferEventL(const TAlfEvent& aEvent) + { + // Ignore other events except pointer events + 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; + } + +// --------------------------------------------------------------------------- +// Handles pointer down events +// --------------------------------------------------------------------------- +// +TBool CAlfExAnalogDialerControl::HandlePointerDownEventL(const TAlfEvent& aEvent) + { + // Topmost image i.e. stopper image visual gets the pointer event + CAlfVisual* onVisual = aEvent.Visual(); + if (onVisual == iStopper) + { + // Is plate rotating starting? + if (IsPointerEventFollowingNumbersArcPath(aEvent)) + { + ResetDraggingData(); + + // Plate rotation is starting + // Register to receive dragging events + Display()->Roster().AddPointerEventObserver(EAlfPointerEventReportDrag, *this); + + // Dragging has started + iFlags |= EAnalogDialerControlDragging; + } + // Is clear key pressed? + else if (IsPointerEventOnClearKey(aEvent)) + { + ResetClearKeyData(); + + if (!(iFlags & EAnalogDialerControlFlagLongTapRegistered)) + { + // Clear key handling needs long tap events + // Register for long tap events + Display()->Roster().AddPointerEventObserver(EAlfPointerEventReportLongTap, *this); + iFlags |= EAnalogDialerControlFlagLongTapRegistered; + } + // Clear key event has started + iFlags |= EAnalogDialerControlClearKeyEvent; + } + } + return ETrue; + } + +// --------------------------------------------------------------------------- +// Handles dragging events +// --------------------------------------------------------------------------- +// +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 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( *iPlate, + iDraggingData.iInitialAngle, + degrees, + iDraggingData.iIsClockWiseOverZeroReached); + } + // else - delta of rotation has not reached KPlateDraggingDelta + } + } + // else - it is permitted that clear key event is dragged outside clear key area. What matters is where + // the pointer event up takes place + return ETrue; + } + +// --------------------------------------------------------------------------- +// Handle pointer up event +// --------------------------------------------------------------------------- +// +TBool CAlfExAnalogDialerControl::HandlePointerUpEventL(const TAlfEvent& aEvent) + { + if (iFlags & EAnalogDialerControlDragging) + { + // Plate rotating has ended. Process result. + FinishDragging(); + } + else if (iFlags & EAnalogDialerControlClearKeyEvent) + { + // clear key event has ended + 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; + } + +// --------------------------------------------------------------------------- +// AddNumberToDisplayPaneL +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::AddNumberToDisplayPaneL(TInt aNumber) + { + if ( aNumber > 9 ) + { + return; + } + TBuf<8> number; + + // aNumber is 0...9 + // Second parameter: number 10 must be changed to 0 + number.Format(KNumberDisplayTemplate, (aNumber+1)%10); + if (iDisplayNumber.Length() + number.Length() <= KNumberDisplayBufferLength) + { + iDisplayNumber.Append(number); + iDisplayPane->SetTextL(iDisplayNumber); + } + } + + +// --------------------------------------------------------------------------- +// SwitchLayoutL +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::SwitchLayoutL() + { + // Get access to first leafs (visuals) of grid layout + CAlfVisual* displayDeck = FindTag(_L8( KTagArray[ETagAnalogDialerDisplayDeck])); + CAlfVisual* dialerDeck = FindTag(_L8( KTagArray[ETagAnalogDialerPlateDeck])); + + if (displayDeck && dialerDeck) + { + // remove visuals from grid layout + iRootLayout->Remove(displayDeck); + iRootLayout->Remove(dialerDeck); + + // resolve new layout + iLayoutMode = ResolveLayout(iSquareSide, + iLongerSide); + + // modify grid layout + AddGridLayoutL(); + + // put visuals back to layout + if (iLayoutMode == ELayoutAnalogDialerPortrait) + { + User::LeaveIfError(iRootLayout->Append(displayDeck)); + User::LeaveIfError(iRootLayout->Append(dialerDeck)); + } + else + { + User::LeaveIfError(iRootLayout->Append(dialerDeck)); + User::LeaveIfError(iRootLayout->Append(displayDeck)); + } + + // Notify Alf about layout change + Env().NotifyLayoutChangedL(); + } + // else - better not to do anything + + iFeedback->Start(); + + // expose new root layout + /*TAlfTimedValue opacity(0.0f); + opacity.SetTarget(255, 500); + iRootLayout->SetOpacity(opacity); */ + + } + +// --------------------------------------------------------------------------- +// Hides the visual +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::PrepareForLayoutSwitchL() + { + /*CAlfVisual* layout = FindTag(_L8(KTagArray[ETagAnalogDialerRootLayout])); + 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 + { + // Resolve whether application is now running in portrait or landscape mode + // Function must also return the shorter extent of the application + // to be used as side length of the square of dialer plate. + // Longer side is returned also to be used in grid weights. + 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 is 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(const TPoint& aPosition) + { + TInt sideLength = aPosition.iX * aPosition.iX; + TInt oppositeLength = aPosition.iY * aPosition.iY; + + TReal hypotenuse; + Math::Sqrt(hypotenuse, sideLength + oppositeLength); + return hypotenuse; + } + + +// --------------------------------------------------------------------------- +// Order of function calls fix the layout of grid in portrait and landscape modes. +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::SetWeightsOfGridL( CAlfGridLayout& aGridLayout, + TLayoutMode aLayout, + TInt aSquareSide, + TInt aLongerSide, + TInt& aPlateTop) + { + // Set up a variable for default value. + RArray weightsDefault; + User::LeaveIfError(weightsDefault.Append(1)); + + // sets weights of grid. Use pizels of application window size to get exact adjustment of pane sizes + RArray weights; + if (aLayout == ELayoutAnalogDialerPortrait) + { + // top: display; take rest what dialer plate leaves for number display + User::LeaveIfError(weights.Append(aLongerSide-aSquareSide)); + + // bottom: dialer, which must be square + User::LeaveIfError(weights.Append(aSquareSide)); + aGridLayout.SetRowsL(weights); + + // default weight for dimension, which is 1. Needed, because weights are reset + aGridLayout.SetColumnsL(weightsDefault); + + // Offset to the plate in Y-dimension is height of number display + aPlateTop = aLongerSide-aSquareSide; + } + else + { + // left: dialer, which must be square + User::LeaveIfError(weights.Append(aSquareSide)); + // right: display; take rest what dialer plate leaves for number display + User::LeaveIfError(weights.Append(aLongerSide-aSquareSide)); + + // default weight for dimension, which is 1. Needed, because weights are reset + 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 below the centre position + // iY: value is position, if position is above 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 + + // aSquare side is length of whole square side + 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 radii. + // Event may be exactly on distance of radii + TBool ret = ETrue; + TInt distanceFromPlateCentre = Hypotenuse(aPlateCentricPoint); + if (distanceFromPlateCentre > aOuterRadius || distanceFromPlateCentre < aInnerRadius) + { + ret = EFalse; + } + return ret; + } + +// --------------------------------------------------------------------------- +// Dragging has finished. Resets dragging data +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::ResetDraggingData() + { + iFlags &= (~EAnalogDialerControlDragging); + iDraggingData.iInitialAngle = 0; + iDraggingData.iPreviousAngle = 0; + iDraggingData.iIsClockWiseOverZeroReached = EFalse; + iDraggingData.iStopperReached=EFalse; + } + +// --------------------------------------------------------------------------- +// Clear key event has finished. Resets dragging data +// --------------------------------------------------------------------------- +// +void CAlfExAnalogDialerControl::ResetClearKeyData() + { + iFlags &= (~EAnalogDialerControlClearKeyEvent); + iFlags &= (~EAnalogDialerControlFlagLongTapPressed); + } + + +// --------------------------------------------------------------------------- +// Turns plate back to idle position i.e. counter clokcwise +// --------------------------------------------------------------------------- +// +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 clockwise +// --------------------------------------------------------------------------- +// +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); + } + +// --------------------------------------------------------------------------- +// 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 + // aCurrentAngle is really current pointer position. KStopper value must be used instead + if (iDraggingData.iIsClockWiseOverZeroReached) + { + deltaAngle = aInitialAngle+(360-KStopperAngle); + } + + // Subtract required minimun angle. + deltaAngle = Abs(deltaAngle)-KPlateMinimumRotateAngle; + if (deltaAngle<0) + { + // plate has not been rotated enough to select any number + 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 +// The path is between edges of plate and clear key area +// --------------------------------------------------------------------------- +// +TBool CAlfExAnalogDialerControl::IsPointerEventFollowingNumbersArcPath( const TAlfEvent& aEvent ) const + { + TPoint plateCentricPoint; + ConvertEventToPlateCentricPosition(aEvent, + iPlateTopOffset, + iSquareSide, + plateCentricPoint); + // Third parameter: Radius of clear area belongs to clear area. Eliminate it. + 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; + // Third parameter: The 'bull eye' of the plate + 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(*iPlate); + 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) + { + // nothing to clear + return; + } + + if (aClearWholeDisplay) + { + // clear whole buf + iDisplayNumber.Zero(); + } + else + { + // delete one number + iDisplayNumber.Delete(lengthNumber-1,1); + } + // changed number to the screen + TRAP_IGNORE(iDisplayPane->SetTextL(iDisplayNumber)); + } + + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +/*void CAlfExAnalogDialerControl::AddEffectLayout() + { + + }*/ + + +// --------------------------------------------------------------------------- +// 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"); + } +