/*
* Copyright (c) 2009, 2010 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:  CDialerToolbarContainer replaces the standard S60 toolbar
*               in the Dialer layout in the Easy dialing mode. It's 
*               responsible of the toolbar layout and logic, but not of the 
*               contents which are provided by an instance of 
*               MPhoneDialerController.
*
*/

#include <aknbutton.h>
#include <aknappui.h>
#include <AknsUtils.h>
#include <aknlayoutscalable_apps.cdl.h>
#include <layoutmetadata.cdl.h>
#include <phoneui.rsg>
#include <phoneappcommands.hrh>
#include <StringLoader.h>
#include <data_caging_path_literals.hrh>

#include "dialercommon.h"
#include "cdialertoolbarcontainer.h"


// CONSTANTS
const TInt KDialerToolbarButtonKeyRepeatDelay = 300;
const TInt KDialerToolbarKeyRepeatInterval = 75;



// CLASS DEFINITION

// ---------------------------------------------------------------------------
// NewL
//
// ---------------------------------------------------------------------------
//
CDialerToolbarContainer* CDialerToolbarContainer::NewL( CCoeControl& aParent,
        MPhoneDialerController* aContentProvider )
    {
    CDialerToolbarContainer* self = 
            new (ELeave) CDialerToolbarContainer( aParent, aContentProvider );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CDialerToolbarContainer
//
// ---------------------------------------------------------------------------
//
CDialerToolbarContainer::CDialerToolbarContainer( CCoeControl& aParent,
        MPhoneDialerController* aContentProvider ) : 
    CDialerContainerBase( aParent ), 
    iContentProvider( aContentProvider )
    {
    }

// ---------------------------------------------------------------------------
// ~CDialerToolbarContainer
//
// ---------------------------------------------------------------------------
//
CDialerToolbarContainer::~CDialerToolbarContainer()
    {
    iCoeEnv->RemoveForegroundObserver( *this );
    DeleteButtons();
    }

// ---------------------------------------------------------------------------
// ConstructL
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::ConstructL()
    {
    BaseConstructL();
    
    CreateButtonsL();

    iCoeEnv->AddForegroundObserverL( *this );
    }

// ---------------------------------------------------------------------------
// SetContentProviderL
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::SetContentProviderL( MPhoneDialerController* aProvider )
    {
    if ( iContentProvider != aProvider )
        {
        iContentProvider = aProvider;
        
        // Recreate buttons to reset available button states
        DeleteButtons();
        CreateButtonsL();
        }
    }

// ---------------------------------------------------------------------------
// UpdateButtonStates
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::UpdateButtonStates()
    {
    // safety check
    if ( iButtons.Count() < MPhoneDialerController::ENumberOfButtons ||
         !iContentProvider )
        {
        return;
        }
    
    for ( TInt i = 0 ; i < MPhoneDialerController::ENumberOfButtons ; ++i )
        {
        MPhoneDialerController::TButtonIndex idx =
            static_cast< MPhoneDialerController::TButtonIndex >( i );
        TInt state = iContentProvider->ButtonState( idx );
        TBool dimmed = iContentProvider->ButtonDimmed( idx );
        if ( state >= 0 )
            {
            iButtons[i]->SetCurrentState( state, EFalse );
            }
        iButtons[i]->SetDimmed( dimmed );
        }
    }

// ---------------------------------------------------------------------------
// SetVariety
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::SetVariety()
    {
    iVariety = ( Layout_Meta_Data::IsLandscapeOrientation() ?
        EDialerVarietyLandscape : EDialerVarietyPortrait );
    }

// ---------------------------------------------------------------------------
// SetLayout
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::SetLayout()
    {
    if ( iButtons.Count() < MPhoneDialerController::ENumberOfButtons )
        {
        return;
        }
    
    // Position for the first button
    TPoint buttonPos = Rect().iTl;
    
    if ( iVariety == EDialerVarietyPortrait )
        {
        // In portrait, buttons are laid out from top to bottom
        for ( TInt i = 0 ; i < iButtons.Count() ; ++i )
            {
            buttonPos = LayoutButton( i, buttonPos );
            }
        }
    else
        {
        // In landscape, buttons are laid out from right to left.
        // This is achieved by looping button array backwards and laying
        // those out from left to right.
        for ( TInt i = iButtons.Count()-1 ; i >= 0  ; --i )
            {
            buttonPos = LayoutButton( i, buttonPos );
            }
        }
    }

// ---------------------------------------------------------------------------
// CountComponentControls
//
// ---------------------------------------------------------------------------
//
TInt CDialerToolbarContainer::CountComponentControls() const
    {
    return iButtons.Count();
    }

// ---------------------------------------------------------------------------
// ComponentControl
//
// ---------------------------------------------------------------------------
//
CCoeControl* CDialerToolbarContainer::ComponentControl( TInt aIndex ) const
    {
    CCoeControl* control = NULL;
    if ( aIndex >= 0 && aIndex < iButtons.Count() )
        {
        control = iButtons[ aIndex ];
        }
    return control;
    }

// ---------------------------------------------------------------------------
// HandleControlEventL
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::HandleControlEventL( CCoeControl* aControl, TCoeEvent aEventType )
    {
    if ( aEventType == MCoeControlObserver::EEventStateChanged )
        {
        // Give dialer a chance to update editor state before handling the button press.
        // Used to reconfigure editor after user has closed virtual keyboard.
        iParentControl.PrepareForFocusGainL();

        // Handle button press. 

        // Multistate buttons change state automatically when pressed. 
        // Revert this change as we do not want to use states
        // for "toggle button" type of functionality but to give one button
        // different functions depending on the situation.
        // Must initiate button redraw so that the state change done here 
        // is drawn too; the automatically changed state is drawn within 
        // AknButton just before EEventStateChanged is reported 
        // (in emulator button icon flashing can be seen).
        UpdateButtonStates();
        aControl->DrawNow();

        // Handle the command attached to the button
        CAknButton* button = static_cast<CAknButton*>( aControl );
        TInt commandId = ButtonCommand( *button );
        
        if ( commandId == EPhoneDialerCmdClear )
            {
            SimulateBackspace();
            }
        else
            {
            // Forward pressed toolbar button to the framework 
            // so that we can handle it in active state
            CAknAppUi* appUi = static_cast<CAknAppUi*>( iCoeEnv->AppUi() );
            appUi->ProcessCommandL( commandId );  
            }
        }
    }

// ---------------------------------------------------------------------------
// HandlePointerEventL
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::HandlePointerEventL( const TPointerEvent& aPointerEvent )
    {
    iLatestPointerEvent = aPointerEvent;
    CCoeControl::HandlePointerEventL( aPointerEvent );
    }

// ---------------------------------------------------------------------------
// HandleGainingForeground
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::HandleGainingForeground()
    {
    // no implementation needed
    }

// ---------------------------------------------------------------------------
// HandleLosingForeground
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::HandleLosingForeground()
    {
    // This is needed when dialer control disappeares during keypress. 
    // Last pressed pointer event must be forwarted to framework 
    // as pointer up event. Thus button is set to unpressed state etc.
    // This code calls HandlePointerEventL of the parent class instead of this.
    // If we call this classes HandlePointerEventL, this classes 
    // grab status is not cleared, and later coming EButton1Up events may end
    // up erroneously to this class, although intended elsewhere.
    // 
    if ( iLatestPointerEvent.iType != TPointerEvent::EButton1Up && Parent() )
        {    
        iLatestPointerEvent.iType = TPointerEvent::EButton1Up;
        TRAP_IGNORE( Parent()->HandlePointerEventL( iLatestPointerEvent ) );
        }    
    }

// ---------------------------------------------------------------------------
// CreateButtonsL
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::CreateButtonsL()
    {
    // Create the avkon button objects
    for ( TInt i = 0 ; i < MPhoneDialerController::ENumberOfButtons ; ++i )
        {
        CAknButton* button = CAknButton::NewLC();
        button->SetContainerWindowL( *this ); // this sets also parent and mopParent
        button->SetObserver( this );
        button->SetIconScaleMode( EAspectRatioPreserved );
        AknsUtils::RegisterControlPosition( button );
        button->ActivateL();
        iButtons.AppendL( button );
        CleanupStack::Pop( button );
        iButtonCommands.AppendL( RArray<TInt>() );
        }

    // Backspace button has auto-repeat feature
    iButtons[MPhoneDialerController::EClearButton]->SetButtonFlags( KAknButtonKeyRepeat );
    iButtons[MPhoneDialerController::EClearButton]->SetKeyRepeatInterval( 
        KDialerToolbarButtonKeyRepeatDelay, KDialerToolbarKeyRepeatInterval );
    
    // Call button doesn't use default button background but a green one 
    // (same as used by in-call view for answer button)
    iButtons[MPhoneDialerController::ECallButton]->SetFrameAndCenterIds( 
            KAknsIIDQsnFrButtonNormalAnswer, KAknsIIDQsnFrButtonNormalAnswerCenter,
            KAknsIIDQsnFrButtonPressedAnswer, KAknsIIDQsnFrButtonPressedAnswerCenter, 
            KAknsIIDQsnFrButtonInactiveAnswer, KAknsIIDQsnFrButtonInactiveAnswerCenter, 
            KAknsIIDQsnFrButtonPressedAnswer, KAknsIIDQsnFrButtonPressedAnswerCenter, 
            KAknsIIDQsnFrButtonInactiveAnswer, KAknsIIDQsnFrButtonInactiveAnswerCenter
            );
        
    // Add button states
    if ( iContentProvider )
        {
        for ( TInt i = 0 ; i < MPhoneDialerController::ENumberOfButtons ; ++i )
            {
            MPhoneDialerController::TButtonIndex btnIdx =
                static_cast< MPhoneDialerController::TButtonIndex >( i );
            RPointerArray< MPhoneDialerController::CButtonData > states;
            CleanupClosePushL( states );
            iContentProvider->GetButtonData( btnIdx, states );
            for ( TInt j = 0 ; j < states.Count() ; ++j )
                {
                TRAP_IGNORE( AddButtonStateL( i, *states[j] ) );
                delete states[j];
                states[j] = NULL;
                }
            CleanupStack::PopAndDestroy( &states );
            }
        }
    }

// ---------------------------------------------------------------------------
// DeleteButtons
// Reset internal arrays holding the button data
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::DeleteButtons()
    {
    for ( TInt i = 0 ; i < iButtons.Count() ; ++i )
        {
        AknsUtils::DeregisterControlPosition( iButtons[i] );
        }
    iButtons.ResetAndDestroy();
    
    for ( TInt i = 0 ; i < iButtonCommands.Count() ; ++i )
        {
        iButtonCommands[i].Reset();
        }
    iButtonCommands.Reset();
    }

// ---------------------------------------------------------------------------
// AddButtonStateL
//
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::AddButtonStateL( TInt aButtonIndex, 
        MPhoneDialerController::CButtonData& aButtonData )
    {
    iButtons[ aButtonIndex ]->AddStateL(
            aButtonData.iIcon,      //CGulIcon* aIcon, 
            NULL,                   //CGulIcon* aDimmedIcon, 
            NULL,                   //CGulIcon* aPressedIcon,
            NULL,                   //CGulIcon* aHoverIcon,
            KNullDesC,              //const TDesC& aText, 
            *aButtonData.iTooltip,  //const TDesC& aHelpText, 
            0 );                    //const TInt aStateFlags
    aButtonData.iIcon = NULL; // ownership transferred
    iButtonCommands[ aButtonIndex ].AppendL( aButtonData.iCommandId );
    }

// ---------------------------------------------------------------------------
// ButtonCommand
//
// ---------------------------------------------------------------------------
//
TInt CDialerToolbarContainer::ButtonCommand( const CAknButton& aButton ) const
    {
    TInt commandId = KErrNotFound;
    
    // Find out index of the button in iButtons array
    TInt btnIndex = iButtons.Find( &aButton );
    __ASSERT_DEBUG( btnIndex >= 0, DialerPanic( EDialerPanicToolbarButtonNotFound ) );
    
    // Find the corresponding command from command array
    TInt btnStateIdx = aButton.StateIndex();
    if ( btnIndex < iButtonCommands.Count() &&
         btnStateIdx < iButtonCommands[btnIndex].Count() )
        {
        commandId = iButtonCommands[btnIndex][btnStateIdx];
        }
    
    __ASSERT_DEBUG( commandId != KErrNotFound, DialerPanic( EDialerPanicToolbarButtonNotFound ) );
    return commandId;
    }

// ---------------------------------------------------------------------------
// LayoutButton
// Set layout for one button.
// ---------------------------------------------------------------------------
//
TPoint CDialerToolbarContainer::LayoutButton( TInt aButtonIdx, TPoint aPos )
    {
    TRect parentRect = Rect();

    TAknWindowComponentLayout buttonLayout = 
        ( aButtonIdx == MPhoneDialerController::ECallButton ? 
          AknLayoutScalable_Apps::cell_dia3_key1_fun_pane( iVariety ) :
          AknLayoutScalable_Apps::cell_dia3_key0_fun_pane( iVariety ) );
        
    TAknLayoutRect layoutRect;
    layoutRect.LayoutRect( parentRect, buttonLayout );
    TRect outerRect = layoutRect.Rect();
    outerRect.SetRect( aPos, outerRect.Size() );

    TRect innerRect = outerRect;
    innerRect.Shrink( 1, 1 );
    iButtons[ aButtonIdx ]->SetRect( innerRect );
    AknsUtils::RegisterControlPosition( iButtons[ aButtonIdx ] );
    
    // Calculate position for the next button
    TPoint nextPos = aPos;
    if ( iVariety == EDialerVarietyPortrait )
        {
        nextPos.iY += outerRect.Height();
        }
    else
        {
        nextPos.iX += outerRect.Width();
        }
    
    return nextPos;
    }

// ---------------------------------------------------------------------------
// SimulateBackspace
// Simulate backspace press including key down event, key event, and key up 
// event
// ---------------------------------------------------------------------------
//
void CDialerToolbarContainer::SimulateBackspace()
    {
    TKeyEvent keyEvent;
    keyEvent.iModifiers = 0;
    keyEvent.iRepeats = 0;
    keyEvent.iCode = 0; // key code is always 0 for key up and down events
    keyEvent.iScanCode = EStdKeyBackspace;

    TRAP_IGNORE( iCoeEnv->SimulateKeyEventL( keyEvent, EEventKeyDown ) );

    keyEvent.iCode = EKeyBackspace;
    TRAP_IGNORE( iCoeEnv->SimulateKeyEventL( keyEvent, EEventKey ) );
    
    keyEvent.iCode = 0;
    TRAP_IGNORE( iCoeEnv->SimulateKeyEventL( keyEvent, EEventKeyUp ) );
    }

// end of file
