exampleapps/alfexanalogdialer/src/alfexanalogdialercontrol.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 07:56:43 +0200
changeset 0 15bf7259bb7c
permissions -rw-r--r--
Revision: 201003

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

// 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; i<KDimensionsOfGrid; i++)
            {
            // resetting non existing weights causes Leave
            TRAP_IGNORE(iRootLayout->RemoveWeightL(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<TInt> weightsDefault;
    User::LeaveIfError(weightsDefault.Append(1));   

    // sets weights of grid. Use pizels of application window size to get exact adjustment of pane sizes
    RArray<TInt> 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");
    }