/*
* Copyright (c) 2005-2007 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:  Phonebook 2 contact info view control.
*
*/


// INCLUDE FILES

#include <CPbk2ContactInfoControl.h>

// Phonebook 2
#include "CPbk2ContactInfoListBox.h"
#include "CPbk2ContactInfoDataSwapper.h"
#include "CPbk2ContactFieldDynamicProperties.h"
#include "CPbk2ThumbnailLoader.h"
#include "Pbk2TitlePanePictureFactory.h"
#include <Pbk2UIControls.rsg>
#include <Pbk2UIControls.hrh>
#include <CPbk2IconArray.h>
#include <MPbk2ContactNameFormatter.h>
#include <CPbk2ViewState.h>
#include <MPbk2ControlObserver.h>
#include <CPbk2PresentationContact.h>
#include <CPbk2PresentationContactField.h>
#include <CPbk2PresentationContactFieldCollection.h>
#include <CPbk2FieldPropertyArray.h>
#include <Pbk2MenuFilteringFlags.hrh>
#include <MPbk2ContactNavigation.h>
#include <CPbk2StorePropertyArray.h>
#include <CPbk2StoreSpecificFieldPropertyArray.h>
#include <MPbk2Command.h>
#include <Pbk2Profile.h>

// Virtual Phonebook
#include <MVPbkContactOperationBase.h>
#include <CVPbkContactManager.h>
#include <MVPbkContactLink.h>
#include <MVPbkStoreContact.h>
#include <MVPbkStoreContactField.h>
#include <MVPbkStoreContactFieldCollection.h>
#include <MVPbkContactStore.h>
#include <MVPbkContactStoreProperties.h>
#include <MVPbkContactFieldSelector.h>
#include <MVPbkFieldType.h>
#include <CVPbkFieldTypeSelector.h>
#include <CVPbkContactStoreUriArray.h>
#include <MVPbkContactFieldTextData.h>
#include <VPbkEng.rsg>

// System includes
#include <akntitle.h>
#include <AknsConstants.h>
#include <avkon.hrh>
#include <barsread.h>
#include <bautils.h>

// Debugging headers
#include <Pbk2Debug.h>


/// Unnamed namespace for local definitions
namespace {

// LOCAL CONSTANTS AND MACROS

const TInt KNumberOfControls = 1;
const TInt KNoContacts = 0;
const TInt KNumberOfContacts = 1;
const TInt KDefaultTitleFormat = MPbk2ContactNameFormatter::EUseSeparator;

#ifdef _DEBUG
enum TPanicCode
    {
    EPanicPreCond_FocusedField = 1,
    EPanicLogic_FocusedField,
    EPanicPreCond_FocusedFieldIndex,
    EPanicPostCond_FocusedFieldIndex,
    EPanicPreCond_FieldPos,
    EPanicPreCond_OfferKeyEventL,
    EPanicPreCond_SizeChanged,
    EPanicPreCond_CountComponentControls,
    EPanicPreCond_ComponentControl
    };

void Panic(TInt aReason)
    {
    _LIT(KPanicText, "CPbk2ContactInfoControl");
    User::Panic(KPanicText, aReason);
    }
#endif // _DEBUG

} /// namespace


// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::CPbk2ContactInfoControl
// --------------------------------------------------------------------------
//
CPbk2ContactInfoControl::CPbk2ContactInfoControl(
        CVPbkContactManager& aManager,
        MPbk2ContactNameFormatter& aNameFormatter,
        MPbk2FieldPropertyArray& aFieldProperties,
        CPbk2StorePropertyArray& aStoreProperties,
        MPbk2ContactNavigation* aNavigator ) :
    iContactManager( aManager ),
    iNameFormatter( aNameFormatter ),
    iNavigator( aNavigator ),
    iFieldProperties( aFieldProperties ),
    iStoreProperties( aStoreProperties ),
    iFocusedFieldIndex( KErrNotFound )
    {
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::~CPbk2ContactInfoControl
// --------------------------------------------------------------------------
//
CPbk2ContactInfoControl::~CPbk2ContactInfoControl()
    {
    if( iTitlePane )
        {
        iTitlePane->SetSmallPicture( NULL, NULL, EFalse );
        }
    if (iCommand)
        {
        // inform the command that the control is deleted
        iCommand->ResetUiControl(*this);
        }
    iObservers.Reset();
    delete iThumbnailLoader;
    delete iListBox;
    delete iContact;
    delete iStoreContact;
    delete iRetriever;
    delete iContactLink;
    delete iSpecificFieldProperties;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ConstructL
// --------------------------------------------------------------------------
//
inline void CPbk2ContactInfoControl::ConstructL(
        const CCoeControl* aContainer)
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2ContactInfoControl::ConstructL begin"));

    SetContainerWindowL(*aContainer);

    // Create the listbox
    iListBox = CPbk2ContactInfoListBox::NewL(
        *this, R_PBK2_CONTACT_INFO_CONTROL, iContactManager);
    iListBox->SetObserver(this);
    iListBox->SetScrollEventObserver( this );
    iListBox->SetListBoxObserver( this );

    // Get flags from listbox
    iFlags = iListBox->Flags();

    // Create thumbnail loader
    iThumbnailLoader = CPbk2ThumbnailLoader::NewL( iContactManager );

    CEikStatusPane* statusPane = iEikonEnv->AppUiFactory()->StatusPane();
    if( statusPane && statusPane->
        PaneCapabilities( TUid::Uid( EEikStatusPaneUidTitle ) ).IsPresent() )
        {
        iTitlePane = static_cast<CAknTitlePane*>(
            statusPane->ControlL( TUid::Uid( EEikStatusPaneUidTitle ) ) );
        }

    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2ContactInfoControl::ConstructL end"));
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2ContactInfoControl* CPbk2ContactInfoControl::NewL
        ( const CCoeControl* aContainer,
          CVPbkContactManager& aManager,
          MPbk2ContactNameFormatter& aNameFormatter,
          MPbk2FieldPropertyArray& aFieldProperties,
          CPbk2StorePropertyArray& aStoreProperties,
          MPbk2ContactNavigation* aNavigator )
    {
    CPbk2ContactInfoControl* self = new ( ELeave ) CPbk2ContactInfoControl
        ( aManager, aNameFormatter, aFieldProperties, aStoreProperties,
          aNavigator );
    CleanupStack::PushL( self );
    self->ConstructL( aContainer );
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::AddObserverL
// --------------------------------------------------------------------------
//
EXPORT_C void CPbk2ContactInfoControl::AddObserverL
        (MPbk2ControlObserver& aObserver)
    {
    User::LeaveIfError(iObservers.Append(&aObserver));
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::RemoveObserver
// --------------------------------------------------------------------------
//
EXPORT_C void CPbk2ContactInfoControl::RemoveObserver
        (MPbk2ControlObserver& aObserver)
    {
    const TInt index = iObservers.Find(&aObserver);
    if (index >= 0)
        {
        iObservers.Remove(index);
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ParentControl
// --------------------------------------------------------------------------
//
MPbk2ContactUiControl* CPbk2ContactInfoControl::ParentControl() const
    {
    // Contact info control has no parent control
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::NumberOfContacts
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::NumberOfContacts() const
    {
    return KNumberOfContacts;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedContactL
// --------------------------------------------------------------------------
//
const MVPbkBaseContact* CPbk2ContactInfoControl::FocusedContactL() const
    {
    return iContact;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedViewContactL
// --------------------------------------------------------------------------
//
const MVPbkViewContact* CPbk2ContactInfoControl::FocusedViewContactL() const
    {
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedStoreContact
// --------------------------------------------------------------------------
//
const MVPbkStoreContact* CPbk2ContactInfoControl::FocusedStoreContact() const
    {
    return iStoreContact;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetFocusedContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetFocusedContactL(
        const MVPbkBaseContact& aContact)
    {
    // Load the new contact
    delete iContactLink;
    iContactLink = NULL;
    iContactLink = aContact.CreateLinkLC();
    CleanupStack::Pop(); // iContactLink

    RetrieveContactL( *iContactLink );
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetFocusedContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetFocusedContactL(
        const MVPbkContactBookmark& /*aContactBookmark*/ )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetFocusedContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetFocusedContactL(
        const MVPbkContactLink& aContactLink )
    {
    // Load the new contact
    delete iContactLink;
    iContactLink = NULL;
    iContactLink = aContactLink.CloneLC();
    CleanupStack::Pop(); // iContactLink

    RetrieveContactL( *iContactLink );
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedContactIndex
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::FocusedContactIndex() const
    {
    return KErrNotSupported;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetFocusedContactIndexL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetFocusedContactIndexL( TInt /*aIndex*/ )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::NumberOfContactFields
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::NumberOfContactFields() const
    {
    return iContact->Fields().FieldCount();
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedField
// --------------------------------------------------------------------------
//
const MVPbkBaseContactField* CPbk2ContactInfoControl::FocusedField() const
    {
    // PreCond:
    __ASSERT_DEBUG(iListBox && iFields, Panic(EPanicPreCond_FocusedField));

    const MVPbkBaseContactField* ret = NULL;
    const TInt fieldsCount = iFields->FieldCount();
    const TInt index = iListBox->CurrentItemIndex();
    TInt numberOfVisibleFields = 0;
    if (index >= 0)
        {
        __ASSERT_DEBUG(index < iFields->FieldCount(),
            Panic(EPanicLogic_FocusedField));
        
        // Field collection may have hidden fields, so find visible field
        // corresponding to the focused field in the listbox.
        for ( TInt i = 0; i < fieldsCount && ret == NULL; i++ )
            {
            if ( iFields->At( i ).IsVisibleInDetailsView() && 
                iFields->At( i ).DynamicVisibility() )
                {
                numberOfVisibleFields++;
                }
            if ( numberOfVisibleFields - 1 == index )
                {
                ret = &iFields->FieldAt( i );
                }
            }
        }

    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedFieldIndex
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::FocusedFieldIndex() const
    {
    // PreCond:
    __ASSERT_DEBUG(iContact, Panic(EPanicPreCond_FocusedFieldIndex));

    return iListBox->CurrentItemIndex();
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetFocusedFieldIndex
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetFocusedFieldIndex(
        TInt aIndex )
    {
    iFocusedFieldIndex = aIndex;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ContactsMarked
// --------------------------------------------------------------------------
//
TBool CPbk2ContactInfoControl::ContactsMarked() const
    {
    // This control does not support contacts marking
    return EFalse;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SelectedContactsL
// --------------------------------------------------------------------------
//
MVPbkContactLinkArray* CPbk2ContactInfoControl::SelectedContactsL() const
    {
    // No selection support in contact info control
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SelectedContactsOrFocusedContactL
// --------------------------------------------------------------------------
//
MVPbkContactLinkArray*
        CPbk2ContactInfoControl::SelectedContactsOrFocusedContactL() const
    {
    CVPbkContactLinkArray* array = CVPbkContactLinkArray::NewLC();
    const MVPbkBaseContact* focusedContact = FocusedContactL();
    if ( focusedContact )
        {
        MVPbkContactLink* link = focusedContact->CreateLinkLC();
        CleanupStack::Pop(); // link
        array->AppendL( link );
        }
    CleanupStack::Pop(); // array

    return array;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SelectedContactsIteratorL
// --------------------------------------------------------------------------
//
MPbk2ContactLinkIterator*
    CPbk2ContactInfoControl::SelectedContactsIteratorL() const
    {
    // No selection support in contact info control
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SelectedContactStoresL
// --------------------------------------------------------------------------
//
CArrayPtr<MVPbkContactStore>*
        CPbk2ContactInfoControl::SelectedContactStoresL() const
    {
    // This is not a contact store control
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ClearMarks
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::ClearMarks()
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetSelectedContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetSelectedContactL(
        TInt /*aIndex*/,
        TBool /*aSelected*/)
    {
    // Not supported in Contact Info
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetSelectedContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetSelectedContactL(
        const MVPbkContactBookmark& /*aContactBookmark*/,
        TBool /*aSelected*/ )
    {
    // Not supported
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetSelectedContactL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetSelectedContactL(
        const MVPbkContactLink& /*aContactLink*/,
        TBool /*aSelected*/ )
    {
    // Not supported
    }

TInt CPbk2ContactInfoControl::CommandItemCount() const
	{
	return 0; // No command items.
	}

const MPbk2UiControlCmdItem& CPbk2ContactInfoControl::CommandItemAt(
	TInt /*aIndex*/ ) const
	{
	// There are no command items in this control
	__ASSERT_ALWAYS( EFalse, User::Panic( _L("Pbk2"), 4)); //TODO
	MPbk2UiControlCmdItem* item = NULL; // For compiler only
	return *item; // For compiler only
	}

const MPbk2UiControlCmdItem* CPbk2ContactInfoControl::FocusedCommandItem() const
	{
	return NULL;
	}

void CPbk2ContactInfoControl::DeleteCommandItemL( TInt /*aIndex*/ )
	{
	// Do nothing
	}

void CPbk2ContactInfoControl::AddCommandItemL(MPbk2UiControlCmdItem* /*aCommand*/, TInt /*aIndex*/)
	{	
	//Do nothing, since there shouldn't be any command items in this state.
	}

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::DynInitMenuPaneL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::DynInitMenuPaneL(
        TInt /*aResourceId*/,
        CEikMenuPane* /*aMenuPane*/) const
    {
    // Nothing to do
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ProcessCommandL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::ProcessCommandL(
        TInt /*aCommandId*/) const
    {
    // Nothing to do
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::UpdateAfterCommandExecution
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::UpdateAfterCommandExecution()
    {
    if( iCommand )
        {
        /// Reset command pointer, command has completed
        iCommand->ResetUiControl(*this);
        iCommand = NULL;
        }
    iListBox->DrawDeferred();
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::GetMenuFilteringFlagsL
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::GetMenuFilteringFlagsL() const
    {
    TInt ret = KPbk2MenuFilteringFlagsNone;

    const TInt fieldCount = FieldCount();
    User::LeaveIfError( fieldCount );
    if ( fieldCount > KNoContacts )
        {
        ret |= KPbk2ListContainsItems;
        }

    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ControlStateL
// --------------------------------------------------------------------------
//
CPbk2ViewState* CPbk2ContactInfoControl::ControlStateL() const
    {
    CPbk2ViewState* state = CPbk2ViewState::NewLC();
    const MVPbkBaseContact* contact = FocusedContactL();

    if ( contact )
        {
        MVPbkContactLink* link = contact->CreateLinkLC();
        state->SetFocusedContact(link);
        CleanupStack::Pop(); // link
        link = NULL; // state has the ownership

        if ( iListBox->Model()->NumberOfItems() > 0 )
            {
            state->SetFocusedFieldIndex( FocusedFieldIndex() );
            const TInt topIndex = iListBox->TopItemIndex();
            if ( topIndex >= 0 )
                {
                const MVPbkStoreContactField& topField =
                    iFields->FieldAt( topIndex );
                state->SetTopFieldIndex( iContact->PresentationFields().
                    FindFieldIndex( topField ) );
                }
            }
        }

    CleanupStack::Pop(); // state
    return state;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::RestoreControlStateL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::RestoreControlStateL(
        CPbk2ViewState* aState )
    {
    const TInt numItems = iListBox->Model()->NumberOfItems();
    const TInt currentItemIndex = iListBox->CurrentItemIndex();

    TInt topFieldIndex( KErrNotFound );
    if ( aState )
        {
        if ( iFocusedFieldIndex == KErrNotFound )
            {
            iFocusedFieldIndex = aState->FocusedFieldIndex();
            topFieldIndex = aState->TopFieldIndex();
            }
        }

    if ( iFocusedFieldIndex >= 0 &&
         iFocusedFieldIndex < numItems )
        {
        iListBox->SetCurrentItemIndex(iFocusedFieldIndex);
        if ( topFieldIndex >= 0 )
            {
            iListBox->SetTopItemIndex( topFieldIndex );
            }
        iListBox->UpdateScrollBarsL();
        }
    else
        {
        iListBox->Reset();

         // Get the field type
        TResourceReader resReader;
        CCoeEnv::Static()->CreateResourceReaderLC
            (resReader, R_PHONEBOOK2_SIP_MSISDN_SELECTOR );

        CVPbkFieldTypeSelector* selector =
            CVPbkFieldTypeSelector::NewL(resReader, iContactManager.FieldTypes());
        CleanupStack::PopAndDestroy(); // resReader
        CleanupStack::PushL( selector );

        const TInt count( iFields->FieldCount() );
        for ( TInt i( 0 ); i < count; ++i )
            {
            const MVPbkFieldType* field = iFields->At( i ).BestMatchingFieldType();
            if ( selector->IsFieldTypeIncluded( *field ) )
                {
                iListBox->SetCurrentItemIndex( i );
                iFocusedFieldIndex = i;
                iListBox->UpdateScrollBarsL();
                break;
                }
            }
        CleanupStack::PopAndDestroy( selector );
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FindTextL
// --------------------------------------------------------------------------
//
const TDesC& CPbk2ContactInfoControl::FindTextL()
    {
    // No find box in this control
    return KNullDesC;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ResetFindL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::ResetFindL()
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ShowThumbnail
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::ShowThumbnail()
    {
    if (iThumbnailLoader && IsFocused() )
        {
        iThumbnailLoader->LoadThumbnail
            ( iStoreContact, iListBox, ELocationUpDown );
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::HideThumbnail
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::HideThumbnail()
    {
    if (iThumbnailLoader)
        {
        iThumbnailLoader->HideThumbnail();
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetBlank
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetBlank(TBool /*aBlank*/)
    {
    // This control does not support blanking
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::RegisterCommand
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::RegisterCommand(
        MPbk2Command* aCommand)
    {
    iCommand = aCommand;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SetTextL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SetTextL( const TDesC& aText )
    {
    iListBox->View()->SetListEmptyTextL( aText );
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ContactUiControlExtension
// --------------------------------------------------------------------------
//
TAny* CPbk2ContactInfoControl::ContactUiControlExtension(TUid aExtensionUid )
	{
     if( aExtensionUid == KMPbk2ContactUiControlExtension2Uid )
        { 
		return static_cast<MPbk2ContactUiControl2*>( this );
		}
		
    return NULL;
    }


// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::UpdateL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::UpdateL(
        MVPbkStoreContact* aContact)
    {
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateL);

    // Update specific field properties first
    delete iSpecificFieldProperties;
    iSpecificFieldProperties = NULL;

    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLCreateStoreSpecificFieldProps);
    iSpecificFieldProperties = CPbk2StoreSpecificFieldPropertyArray::NewL(
        iFieldProperties,
        iStoreProperties,
        iContactManager.FieldTypes(),
        aContact->ParentStore());
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLCreateStoreSpecificFieldProps);

    // Create an presentation contact from the store contact
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLCreatePresentationContact);
    CPbk2PresentationContact* presentation =
        CPbk2PresentationContact::NewL(*aContact, *iSpecificFieldProperties);
    CleanupStack::PushL(presentation);
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLCreatePresentationContact);
    
    // Create title pane picture
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLCreateTitlePanePicture);
    CEikImage* image = Pbk2TitlePanePictureFactory::
        CreateTitlePanePictureLC( aContact, iStoreProperties );
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLCreateTitlePanePicture);

    // Instantiate a helper object
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLCreateSwapper);
    CPbk2ContactInfoDataSwapper* swapper =
        new(ELeave) CPbk2ContactInfoDataSwapper;
    CleanupStack::PushL(swapper);
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLCreateSwapper);

    // Store the members which are to be swapped
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLStorePanes);
    StorePanesL(*swapper, presentation);
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLStorePanes);

    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLCreateDynamicFieldProperties);
    CPbk2ContactFieldDynamicProperties* filter =
        CreateDynamicFieldPropertiesLC( *presentation );
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLCreateDynamicFieldProperties);

    // Prepare listbox for state transitition
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLPrepareForUpdate);
    iListBox->PrepareForUpdateL
        ( *swapper, presentation->PresentationFields(),
            *iSpecificFieldProperties, filter );
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLPrepareForUpdate);

    CleanupStack::Pop();  // filter

    // All code that might leave has been executed succesfully, change state
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLSetTitlePane);
    if( iTitlePane )
        {
        // Set title pane text
        if( swapper->iTitlePaneText )
            {
            iTitlePane->SetText(swapper->iTitlePaneText);
            swapper->iTitlePaneText = NULL;
            }
        // Set title pane picture
        // picture ownership is transfered to title pane
        image->SetPictureOwnedExternally( ETrue );
        iTitlePane->SetSmallPicture(
            image->Bitmap(), image->Mask(), image->Bitmap() != NULL );
        }
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLSetTitlePane);

    // Update listbox
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLUpdateListbox);
    TRAPD(err, iListBox->DoUpdateL(*swapper, *aContact));
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLUpdateListbox);

    // Cleanup and change state
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLCleanupState);
    CleanupStack::PopAndDestroy(swapper);
    // bitmap and mask from image were transfered to title pane,
    // so image can be destroyed now
    CleanupStack::PopAndDestroy( image );
    CleanupStack::Pop(presentation);
    delete iContact;
    iContact = presentation;
    iFields = &presentation->PresentationFields();
    User::LeaveIfError(err);
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLCleanupState);

    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLSetIndex);
    if ( iFocusedFieldIndex >= 0 &&
         iFocusedFieldIndex < iListBox->Model()->NumberOfItems() )
        {
        iListBox->SetCurrentItemIndex(iFocusedFieldIndex);
        }
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLSetIndex);

    // Update navigators
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLUpdateNavigators);
    if ( iNavigator )
        {
        MVPbkContactLink* contactLink = aContact->CreateLinkLC();
        if ( contactLink )
            {
            iNavigator->UpdateNaviIndicatorsL(*contactLink);
            }
        CleanupStack::PopAndDestroy(); // contactLink
        }
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLUpdateNavigators);

    DrawDeferred();

    // To avoid double deletion, the store contact ownership
    // is transferred after everything that can leave has been
    // executed
    delete iStoreContact;
    iStoreContact = aContact;
            
    // Notify control readiness
    PBK2_PROFILE_START(Pbk2Profile::EContactInfoControlUpdateLSendEvents);
    TRAPD( error, SendEventToObserversL( TPbk2ControlEvent::EReady ) );
    if ( error != KErrNone )
        {
        // When function leaves the aContact will be
        // deleted through CleanupStack by calling function.
        iStoreContact = NULL;
        User::Leave( error );
        }

    SetFocus( ETrue );
    
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateLSendEvents);
    PBK2_PROFILE_END(Pbk2Profile::EContactInfoControlUpdateL);
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FieldCount
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::FieldCount() const
    {
    TInt ret = KErrNotReady;
    if ( iFields )
        {
        ret = iFields->FieldCount();
        }
    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FieldPos
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::FieldPos(
        const MVPbkStoreContactField& aField) const
    {
    // PreCond:
    __ASSERT_DEBUG(iListBox && iFields, Panic(EPanicPreCond_FieldPos));

    TInt ret = KErrNotFound;
    const TInt count(iFields->FieldCount());
    for (TInt i = 0; i < count; ++i)
        {
        if (iFields->At(i).IsSame(aField))
            {
            ret = i;
            break;
            }
        }
    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::VPbkSingleContactOperationComplete
// --------------------------------------------------------------------------
//

void CPbk2ContactInfoControl::VPbkSingleContactOperationComplete(
        MVPbkContactOperationBase& aOperation,
        MVPbkStoreContact* aContact)
    {
    if (aContact && &aOperation == iRetriever)
        {
        // contact loading completed, update control data
        TRAPD(err, UpdateL(aContact));
        if (err != KErrNone)
            {
            delete aContact;
            CCoeEnv::Static()->HandleError(err);
            }

        delete iRetriever;
        iRetriever = NULL;
        delete iContactLink;
        iContactLink = NULL;
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::VPbkSingleContactOperationFailed
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::VPbkSingleContactOperationFailed(
        MVPbkContactOperationBase& /*aOperation*/,
        TInt aError)
    {
    CCoeEnv::Static()->HandleError(aError);
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusedItemPointed
// --------------------------------------------------------------------------
//
TBool CPbk2ContactInfoControl::FocusedItemPointed()
    {
    return iListBox->FocusedItemPointed();
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusableItemPointed
// --------------------------------------------------------------------------
//
TBool CPbk2ContactInfoControl::FocusableItemPointed()
    {
    return iListBox->FocusableItemPointed();
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SearchFieldPointed
// --------------------------------------------------------------------------
//
TBool CPbk2ContactInfoControl::SearchFieldPointed()
    {
    return iListBox->SearchFieldPointed();
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::OfferKeyEventL
// --------------------------------------------------------------------------
//
TKeyResponse CPbk2ContactInfoControl::OfferKeyEventL(
        const TKeyEvent& aKeyEvent,
        TEventCode aType)
    {
    __ASSERT_DEBUG(iListBox, Panic(EPanicPreCond_OfferKeyEventL));

    if ( aType == EEventKey )
        {
        // Reset iFocusedFieldIndex
        // UpdateL method called after editor is closed and correct field
        // is focused. Focus jump, if user same time than UpdateL method is
        // called, scrolling fields. That's why this iFocusedFieldIndex
        // reseting.
        iFocusedFieldIndex = KErrNotFound;
        }

    // Forward all key events to the list box
    TKeyResponse response = iListBox->OfferKeyEventL(aKeyEvent,aType);
    
    // Ignore Send Key up and down events to prevent Dialer appearance
    // on top of Phonebook application.
    if ( response == EKeyWasNotConsumed && IsFocused() )
        {
        if ( ( aType == EEventKeyDown || aType == EEventKeyUp )
            && aKeyEvent.iScanCode == EStdKeyYes )
            {
            response = EKeyWasConsumed;
            }
        }
        
/*
HandleControlEventL does this already...no need for double update...plus the HandleControlEventL works also for touch...
    // Let the listbox first handle the key event before drawing the
    // thumbnail.
    if ( iThumbnailLoader && aType == EEventKey &&
        (aKeyEvent.iScanCode == EStdKeyUpArrow ||
        aKeyEvent.iScanCode == EStdKeyDownArrow ))
        {
        RDebug::Print( _L("### info view: OfferKeyEventL!") );
        iThumbnailLoader->Refresh();
        }
*/
    return response;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SizeChanged
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SizeChanged()
    {
    __ASSERT_DEBUG(iListBox, Panic(EPanicPreCond_SizeChanged));
    // Listbox occupies whole area of this control
    iListBox->SetRect(Rect());
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::CountComponentControls
// --------------------------------------------------------------------------
//
TInt CPbk2ContactInfoControl::CountComponentControls() const
    {
    __ASSERT_DEBUG(iListBox, Panic(EPanicPreCond_CountComponentControls));
    
    // Always only one child control: the list box.
    return KNumberOfControls;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::ComponentControl
// --------------------------------------------------------------------------
//
CCoeControl* CPbk2ContactInfoControl::ComponentControl(
        TInt PBK2_DEBUG_ONLY(aIndex)) const
    {
    __ASSERT_DEBUG(iListBox && aIndex==0,
        Panic(EPanicPreCond_ComponentControl));

    // Return the one and only child control
    return iListBox;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::FocusChanged
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::FocusChanged(
        TDrawNow aDrawNow )
    {
    if ( iListBox )
        {
        iListBox->SetFocus( IsFocused(), aDrawNow );
        }

    if ( IsFocused() )
        {
        ShowThumbnail();
        }
    else
        {
        HideThumbnail();
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::HandleResourceChange
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::HandleResourceChange(
        TInt aType)
    {
    CCoeControl::HandleResourceChange(aType);
    TRAP_IGNORE(DoHandleResourceChangeL(aType));
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::HandlePointerEventL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::HandlePointerEventL(
    const TPointerEvent& aPointerEvent )
    {
    CCoeControl::HandlePointerEventL( aPointerEvent );
    if ( AknLayoutUtils::PenEnabled() )
        {
        switch( aPointerEvent.iType )
            {
            case TPointerEvent::EButton1Up:
                {
                if ( iListBox != NULL && iListBox->Rect().Contains(
                    aPointerEvent.iPosition ) )
                    {
                    iFocusedFieldIndex = iListBox->CurrentItemIndex();
                    }
                break;
                }

            default:
                {
                break;
                }
            };
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::HandleControlEventL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::HandleControlEventL(
        CCoeControl* aControl,
        TCoeEvent aEventType)
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2ContactInfoControl::HandleControlEventL(0x%x,0x%x,%d)"),
        this, aControl, aEventType);
    if ( iThumbnailLoader && (aControl == iListBox) && IsFocused() )
        {
        iThumbnailLoader->Refresh();
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::StorePanesL
// --------------------------------------------------------------------------
//
inline void CPbk2ContactInfoControl::StorePanesL(
        CPbk2ContactInfoDataSwapper& aSwapper,
        const CPbk2PresentationContact* aContact)
    {
    if( iFlags & KPbk2ContactInfoControlUpdateStatusPane )
        {
        if( (iFlags & KPbk2ContactInfoControlUpdateTitlePane) && iTitlePane )
            {
            // Store title pane text
            if (aContact)
                {
                aSwapper.iTitlePaneText = iNameFormatter.GetContactTitleL(
                    aContact->Fields(), KDefaultTitleFormat);
                }
            }
        if( iFlags & KPbk2ContactInfoControlUpdateContextPane )
            {
            if (aContact)
				{
	            iThumbnailLoader->LoadThumbnail(&aContact->StoreContact(),
	                                            iListBox,
	                                            ELocationUpDown);
				}
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::SendEventToObserversL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::SendEventToObserversL(
        const TPbk2ControlEvent& aEvent)
    {
    // Loop backwards in case some observer destroys itself in the
    // event handler
    for (TInt i = iObservers.Count()-1; i >= 0; --i)
        {
        iObservers[i]->HandleControlEventL(*this, aEvent);
        }
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::CreateDynamicFieldPropertiesLC
// --------------------------------------------------------------------------
//
CPbk2ContactFieldDynamicProperties*
    CPbk2ContactInfoControl::CreateDynamicFieldPropertiesLC(
        CPbk2PresentationContact& aContact )
    {
    CPbk2ContactFieldDynamicProperties* filter =
        CPbk2ContactFieldDynamicProperties::NewLC();
    // Check is there ringtone field and if there is, check that file is still
    // available. If not, hide the field.
    TInt fieldCount( aContact.Fields().FieldCount() );
    for ( TInt i = 0; i < fieldCount; ++i )
        {
        const MVPbkStoreContactField& field = aContact.Fields().FieldAt( i );
        TInt resId( field.BestMatchingFieldType()->FieldTypeResId() );
        if ( resId == R_VPBK_FIELD_TYPE_RINGTONE )
            {
            TPtrC filename =
                MVPbkContactFieldTextData::Cast( field.FieldData() ).Text();
            if ( !BaflUtils::FileExists(
                CCoeEnv::Static()->FsSession(),
                filename ) )
                {
                filter->AddHiddenFieldResourceId( resId );
                aContact.PresentationFields().At( i ).
                    SetDynamicVisibility( EFalse );
                break;
                }
            }
        }

    return filter;
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::DoHandleResourceChangeL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::DoHandleResourceChangeL(
        TInt aType)
    {
    if (iListBox)
        {
        iListBox->DoHandleResourceChangeL(aType);
        }
/*  This is useless...no need to show or hide thumbnail on layout/skin change...thumbnail will handle it's own resouce change event
    if ( aType == KEikDynamicLayoutVariantSwitch ||
         aType == KAknsMessageSkinChange )
        {
        if ( IsFocused() )
            {
            RDebug::Print( _L("### InfoControl::HandleResourceChangeL - show") );
            ShowThumbnail();
            }
        else
            {
            RDebug::Print( _L("### InfoControl::HandleResourceChangeL - hide") );
            HideThumbnail();
            }
        }*/
    }

// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::RetrieveContactL
// --------------------------------------------------------------------------
//    
void CPbk2ContactInfoControl::RetrieveContactL( 
        const MVPbkContactLink& aContactLink )
    {
    delete iRetriever;
    iRetriever = NULL;
    iRetriever = iContactManager.RetrieveContactL( aContactLink, *this );
    }

// --------------------------------------------------------------------------
// 
// --------------------------------------------------------------------------
//    
void CPbk2ContactInfoControl::HandleScrollEventL(CEikScrollBar* /*aScrollBar*/, TEikScrollEvent aEventType)
    {


    switch( aEventType )
        {
        // drag events -> hide thumbnail
        // AL: this isn't very efficient way of doing this...
        // ...thumbnail loader interface should be extended to 
        // enable thumbnail window to be hidden/shown without 
        // destroying and reloading the bitmaps...
        case EEikScrollThumbDragHoriz:
        case EEikScrollThumbDragVert:
            {
            iDidMove = ETrue;
            HideThumbnail();
            break;
            }
            
        // release events
        case EEikScrollThumbReleaseHoriz:
        case EEikScrollThumbReleaseVert:
            {
            // AL: this isn't very efficient way of doing this...
            // ...thumbnail loader interface should be extended to 
            // enable thumbnail window to be hidden/shown without 
            // destroying and reloading the bitmaps...
            if( iDidMove )
                {
                ShowThumbnail();
                iDidMove = EFalse;
                }
            break;
            }

        default:
            {
            iDidMove= EFalse;
            if( iThumbnailLoader )
                {
                iThumbnailLoader->Refresh();
                }
            }
        }
    }
// --------------------------------------------------------------------------
// CPbk2ContactInfoControl::HandleListBoxEventL
// --------------------------------------------------------------------------
//
void CPbk2ContactInfoControl::HandleListBoxEventL(
    CEikListBox* /*aListBox*/, TListBoxEvent aEventType )
    {
    switch ( aEventType )
        {
        case EEventItemDoubleClicked:
            SendEventToObserversL( TPbk2ControlEvent::EContactDoubleTapped );
            break;
        case EEventItemSingleClicked:
            SendEventToObserversL( TPbk2ControlEvent::EContactTapped );
            break;   
        case EEventEnterKeyPressed:
            SendEventToObserversL( TPbk2ControlEvent::EControlEnterKeyPressed );
            break;  
        default:
            ;
        }
    }


//  End of File
