phonebookui/Phonebook/View/src/CPbkContactViewListControl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 10:12:17 +0200
changeset 0 e686773b3f54
child 21 9da50d567e3c
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
* Copyright (c) 2002 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: 
*       Provides methods for phonebook contact view list control.
*
*/


// INCLUDE FILES
#include <CPbkContactViewListControl.h>  // This class
#include <barsread.h>    // TResourceReader
#include <eikclbd.h>     // CColumnListBoxData
#include <aknsfld.h>     // CAknSearchField
#include <aknlists.h>    // AknListBoxUtils
#include <AknsDrawUtils.h>
#include <featmgr.h>
#include <bldvariant.hrh>
#include <PbkView.rsg>
#include <AknDef.h>      // KEikDynamicLayoutVariantSwitch
#include <AknUtils.h>


// PbkView classes
#include <PbkView.hrh>
#include "CPbkViewState.h"
#include "CPbkThumbnailPopup.h"
#include <CPbkIconArray.h>
#include <MPbkContactViewListControlObserver.h>
#include "PbkFindPrimitivesFactory.h"
#include "PbkContactViewListModelFactory.h"
#include "MPbkFetchDlgSelection.h"

// PbkEng classes
#include <CPbkContactEngine.h>
#include <CPbkConstants.h>
#include <CPbkContactFindView.h>

// PbkExt classes
#include <CPbkExtGlobals.h>
#include <MPbkViewExtension.h>
#include <MPbkContactUiControlExtension.h>
#include <MPbkExtensionFactory.h>

// Phonebook common include files
#include <PbkDebug.h>        // Phonebook debugging support


// Unnamed namespace for local definitions
namespace {

// LOCAL CONSTANTS AND MACROS

/// Listbox model's entry cache size
const TInt KCacheSize = 32;

const TUint16 KZwsChar = 0x200b;
const TUint16 KZwnjChar = 0x200c;

enum TPanicCode
    {
    EPanicPostCond_Constructor = 1,
    EPanicPreCond_ConstructFromResourceL,
    EPanicInvalidListBoxType_ConstructFromResourceL,
    EPanicPostCond_ConstructFromResource,
    EPanicPreCond_ConstructL,
    EPanicPostCond_ConstructL,
    EPanicListBoxNull,
    EPanicLogic_ComponentControl,
    EPanicPostCond_EnableFindBoxL,
    EPanicPostCond_DisableFindBoxL,
    EPanicLogic_FindTextL,
    EPanicPre_SetControlExtensionL
    };


// MODULE DATA STRUCTURES

NONSHARABLE_CLASS(TCleanupEnableListBoxViewRedraw)
    {
    public:  // Interface
        inline TCleanupEnableListBoxViewRedraw(CListBoxView& aListBoxView)
            : iCleanupItem(CleanupOp,&aListBoxView)
            {
            }

        inline operator TCleanupItem() const
            {
            return iCleanupItem;
            }

    private:  // Implementation
        static void CleanupOp(TAny* aPtr);

    private:  // Data
        TCleanupItem iCleanupItem;
    };

// ==================== LOCAL FUNCTIONS ====================

/**
 * Returns index of aValue in aArray or KErrNotFound.
 */
template<class T>
TInt Find(const CArrayFix<T>& aArray, const T& aValue)
    {
    const TInt count = aArray.Count();
    for (TInt i = 0; i < count; ++i)
        {
        if (aArray[i] == aValue)
            {
            return i;
            }
        }
    return KErrNotFound;
    }

/**
 * Returns CPbkIconArray icon array from aListBox.
 */
inline CPbkIconArray* IconArray(CEikColumnListBox& aListBox)
    {
    return static_cast<CPbkIconArray*>
        (aListBox.ItemDrawer()->ColumnData()->IconArray());
    }

/**
 * Sets aIconArray as aListBox'es icon array.
 */
inline void SetIconArray
        (CEikColumnListBox& aListBox, CPbkIconArray* aIconArray)
    {
    aListBox.ItemDrawer()->ColumnData()->SetIconArray(aIconArray);
    }

void Panic(TPanicCode aReason)
    {
    _LIT(KPanicText, "CPbkContactViewListControl");
    User::Panic(KPanicText, aReason);
    }

/**
 * Returns ETrue, if a character is to be included in the search string
 */
inline TBool IsSearchableChar( const TChar aChar )
    {
    switch (aChar)
        {
        case KZwsChar:  // FALLTHROUGH
        case KZwnjChar:
            {
            return EFalse;
            }
        }
    return ETrue;
    }

// MACROS

/// Define this to print find performance data to debug output
#ifdef PBK_BENCHMARK_FIND
  #pragma message("Warning: PBK_BENCHMARK_FIND is set")
#endif

// MODULE DATA STRUCTURES

#ifdef PBK_BENCHMARK_FIND

/// Benchmarking helper class.
template<class NumT>
NONSHARABLE_CLASS(TRunningTimes)
    {
    public:  // Interface
        /**
         * Constructor.
         */
        inline TRunningTimes()
            {
            Reset();
            }
        /**
         * () operator.
         */
        inline NumT operator()() const
            {
            return iTotal;
            }

        inline void Start()
            {
            iStart.UniversalTime();
            }
        inline void Stop()
            {
            iStop.UniversalTime();
            const TInt timeDiff =
                I64LOW(iStop.MicroSecondsFrom(iStart).Int64()) / 1000;
            iTotal += timeDiff;
            iCount++;
            }

        inline TInt Count() const
            {
            return iCount;
            }

        /**
         * Returns total time
         */
        inline NumT Total() const
            {
            return iTotal;
            }

        /**
         * Reset.
         */
        inline void Reset()
            {
            iTotal=0; iCount=0;
            }

    private:  // Data
        TTime iStart;
        TTime iStop;
        /// Own: count
        TInt iCount;
        /// Own: total time
        NumT iTotal;
    };

#endif  // PBK_BENCHMARK_FIND

}  // namespace


// ================= MEMBER FUNCTIONS =======================

//
// CPbkContactViewListControl
//
inline CEikColumnListBox& CPbkContactViewListControl::ListBox() const
    {
    __ASSERT_DEBUG(iListBox, Panic(EPanicListBoxNull));
    return(*iListBox);
    }

inline MPbkContactViewListModel& CPbkContactViewListControl::Model() const
    {
    return(*static_cast<MPbkContactViewListModel*>
        (iListBox->Model()->ItemTextArray()));
    }

inline TBool CPbkContactViewListControl::PostCond_Constructor()
    {
    return
        (!iListBox && !iFindBox && !iFindTextBuf && !iBaseView && !iView &&
        !iResourceData.iUnnamedText && !iResourceData.iFindEmptyText &&
        !iMarkedItemsArray && !iThumbnailPopup);
    }

inline TBool CPbkContactViewListControl::PostCond_ConstructFromResource()
    {
    return
        (iListBox && !iFindBox && !iFindTextBuf &&
        iResourceData.iUnnamedText && !iMarkedItemsArray);
    }

EXPORT_C CPbkContactViewListControl* CPbkContactViewListControl::NewL
        (CPbkContactEngine& aEngine,
        CContactViewBase& aView,
        TInt aResourceId,
        const CCoeControl* aParent)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactViewListControl::NewL()"));
    CPbkContactViewListControl* self = new(ELeave) CPbkContactViewListControl();
    CleanupStack::PushL(self);
    self->ConstructL(aEngine, aView, aResourceId, aParent);
    CleanupStack::Pop(self);
    return self;
    }

EXPORT_C CPbkContactViewListControl::CPbkContactViewListControl() :
    iLastFocusId(KNullContactId),
    iChangedIndexes(1 /*allocation granularity*/)
    {
    // CBase::operator new(TLeave) resets other members
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::CPbkContactViewListControl(0x%x)"),
            this);
    __ASSERT_DEBUG(PostCond_Constructor(), Panic(EPanicPostCond_Constructor));
    }

void CPbkContactViewListControl::ConstructFromResourceL
        (TResourceReader& aReader)
    {
    __ASSERT_DEBUG(PostCond_Constructor(),
        Panic(EPanicPreCond_ConstructFromResourceL));
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::ConstructFromResourceL(0x%x)"),this);

    // emptyText
    TPtrC emptyText = aReader.ReadTPtrC();
    // unnamedText
    iResourceData.iUnnamedText = aReader.ReadHBufCL();
    // flags
    iResourceData.iFlags = aReader.ReadUint32();

    // Empty text in find state
    TResourceReader rr;
    iEikonEnv->CreateResourceReaderLC(rr, R_AVKON_FIND_NO_MATCHES);
    iResourceData.iFindEmptyText = rr.ReadHBufCL();
    CleanupStack::PopAndDestroy(); // rr

    // listboxType
    TInt listBoxType = aReader.ReadInt16();
    iListBox = static_cast<CEikColumnListBox*>
        (EikControlFactory::CreateByTypeL(listBoxType).iControl);
    __ASSERT_ALWAYS(iListBox, Panic
        (EPanicInvalidListBoxType_ConstructFromResourceL));
    iListBox->SetContainerWindowL(*this);
    // listbox
    iListBox->ConstructFromResourceL(aReader);
    iListBox->View()->SetListEmptyTextL(emptyText);
    iListBox->CreateScrollBarFrameL(ETrue);
    iListBox->ScrollBarFrame()->SetScrollBarVisibilityL
        (CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto);
    iListBox->SetObserver(this);
    iListBox->MakeVisible(EFalse);

    // iconArray, use granularity of 4

    CPbkIconArray* iconArray = new(ELeave) CPbkIconArray(4);
    CleanupStack::PushL(iconArray);
    const TInt iconArrayRes = aReader.ReadInt32();
    iconArray->ConstructFromResourceL(iconArrayRes);

    // get extension factory for setting extension icons
    CPbkExtGlobals* extGlobal = CPbkExtGlobals::InstanceL();
    extGlobal->PushL();
    MPbkExtensionFactory& factory = extGlobal->FactoryL();
    factory.AddPbkFieldIconsL(NULL, iconArray);
    CleanupStack::PopAndDestroy(extGlobal);

    CleanupStack::Pop(iconArray);

    SetIconArray(*iListBox,iconArray);

    // emptyIconId
    iResourceData.iEmptyIconId =
        static_cast<TPbkIconId>(aReader.ReadInt16());
    // defaultIconId
    iResourceData.iDefaultIconId =
        static_cast<TPbkIconId>(aReader.ReadInt16());

    // Preallocate space for the one required integer
    iChangedIndexes.AppendL(-1);

    __ASSERT_DEBUG(PostCond_ConstructFromResource(),
        Panic(EPanicPostCond_ConstructFromResource));
    }

EXPORT_C void CPbkContactViewListControl::ConstructL
        (CPbkContactEngine& aEngine,
        CContactViewBase& aView)
    {
    // Check that ConstructFromResourceL is executed
    __ASSERT_DEBUG(PostCond_ConstructFromResource() && !iBaseView && !iView,
        Panic(EPanicPreCond_ConstructL));
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::ConstructL(0x%x)"),this);

    iFindPrimitives = PbkFindPrimitivesFactory::CreateL(
        aEngine.ContactNameFormat());
    iBaseView = &aView;
    iView = CPbkContactFindView::NewL(
        aEngine, *iBaseView, *this, *iFindPrimitives);

    // Set aParams for MPbkContactViewListModel
    PbkContactViewListModelFactory::TParams params;
    params.iEngine = &aEngine;
    params.iView = iView;
    params.iCacheSize = KCacheSize;
    params.iIconArray = IconArray(*iListBox);
    params.iEmptyId = iResourceData.iEmptyIconId;
    params.iDefaultId = iResourceData.iDefaultIconId;

    // Listbox model
    MPbkContactViewListModel* model = PbkContactViewListModelFactory::
                                            CreateL(params);
    model->SetUnnamedText(iResourceData.iUnnamedText);
    iListBox->Model()->SetItemTextArray(model);
    iListBox->Model()->SetOwnershipType(ELbmOwnsItemArray);

    // thumbnail handler
    if (iResourceData.iFlags & KPbkContactViewListControlUpdateContextPane)
        {
        iThumbnailPopup = CPbkThumbnailPopup::NewL(aEngine);
        }

    // Sets up TLS, must be done before FeatureManager is used.
    FeatureManager::InitializeLibL();
    // iEngine != NULL means that Featuremanager has been initialized
    iEngine = &aEngine;

    __ASSERT_DEBUG(PostCond_ConstructFromResource() && iBaseView && iView &&
        iThumbnailPopup || !(iResourceData.iFlags &
        KPbkContactViewListControlUpdateContextPane),
        Panic(EPanicPostCond_ConstructL));
    }

CPbkContactViewListControl::~CPbkContactViewListControl()
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::~CPbkContactViewListControl(0x%x)"),
        this);

    // This should be done before listbox delete
    // (which deletes dummy control extension)
    Release(iControlExtension);

    // Fetch dialog pages might delay calling ConstructL and thus Feature
    // manager might not be initialized if destructing early.
    if (iEngine)
        {
        // iEngine != NULL means that Featuremanager has been initialized
        FeatureManager::UnInitializeLib();
        }

    delete iOriginalEmptyText;
    iObservers.Reset();
    delete iFindTextBuf;
    if (iView)
        {
        iView->Close(*this);
        }
    delete iFindPrimitives;
    delete iThumbnailPopup;
    delete iMarkedItemsArray;
    delete iFindBox;
    delete iListBox;
    delete iResourceData.iFindEmptyText;
    delete iResourceData.iUnnamedText;
    }

EXPORT_C TContactItemId CPbkContactViewListControl::ContactIdAtL
        (TInt aIndex) const
    {
    return iView->AtL(aIndex);
    }

EXPORT_C TInt CPbkContactViewListControl::FindContactIdL
        (TContactItemId aId) const
    {
    return iView->FindL(aId);
    }

EXPORT_C TInt CPbkContactViewListControl::CurrentItemIndex() const
    {
    return ListBox().CurrentItemIndex();
    }

EXPORT_C void CPbkContactViewListControl::SetCurrentItemIndex(TInt aIndex)
    {
    if (ListBox().CurrentItemIndex() != aIndex)
        {
        ListBox().SetCurrentItemIndex(aIndex);
        HandleFocusChange();
        }
    }

EXPORT_C void CPbkContactViewListControl::SetCurrentItemIndexAndDraw
        (TInt aIndex)
    {
    if (ListBox().CurrentItemIndex() != aIndex)
        {
        ListBox().SetCurrentItemIndexAndDraw(aIndex);
        HandleFocusChange();
        }
    }

EXPORT_C void CPbkContactViewListControl::HandleMarkableListProcessCommandL
        (TInt aCommandId)
    {
    AknSelectionService::HandleMarkableListProcessCommandL
        (aCommandId, &ListBox());
    }

EXPORT_C void CPbkContactViewListControl::HandleMarkableListDynInitMenuPane
        (TInt aResourceId,
        CEikMenuPane *aMenu)
    {
    AknSelectionService::HandleMarkableListDynInitMenuPane
        (aResourceId, aMenu, &ListBox());
    }

EXPORT_C void
    CPbkContactViewListControl::HandleMarkableListUpdateAfterCommandExecution()
    {
    AknSelectionService::HandleMarkableListUpdateAfterCommandExecution
        (&ListBox());
    }

EXPORT_C const TDesC& CPbkContactViewListControl::FindTextL() const
    {
    // Initial minimum size for the find text buffer
    const TInt KInitialBufSize = 8;

    if (iFindBox)
        {
        const TInt findBoxTextLength = iFindBox->TextLength();
        if (findBoxTextLength > 0)
            {
            TInt bufCapacity = 0;
            if (iFindTextBuf)
                {
                bufCapacity = iFindTextBuf->Des().MaxLength();
                }
            if (bufCapacity < findBoxTextLength)
                {
                // Allocate a new buffer of at least KInitialBufSize
                // characters or twice as large as the previous one
                const TInt newBufSize = Max(Max(KInitialBufSize,2*bufCapacity),
                    findBoxTextLength);
                HBufC* newBuf = HBufC::NewL(newBufSize);
                delete iFindTextBuf;
                iFindTextBuf = newBuf;
                }
            TPtr bufPtr = iFindTextBuf->Des();
            __ASSERT_DEBUG
                (bufPtr.MaxLength()>=KInitialBufSize && bufPtr.MaxLength()>=findBoxTextLength,
                Panic(EPanicLogic_FindTextL));
            iFindBox->GetSearchText(bufPtr);
            // Strip from search string all unwanted characters that e.g. Hindi input places there
            for( TInt i = bufPtr.Length()-1; i >= 0; --i )
                {
                if (!IsSearchableChar(bufPtr[i]))
                    {
                    bufPtr.Delete( i, 1 );
                    }
                }
            return(*iFindTextBuf);
            }
        }
    return KNullDesC;
    }

EXPORT_C void CPbkContactViewListControl::ResetFindL()
    {
    if (iFindBox && iFindBox->TextLength() > 0)
        {
        iFindBox->ResetL();
        UpdateFindResultL();
        iFindBox->DrawDeferred();
        }
    }

EXPORT_C TBool CPbkContactViewListControl::ItemMarked(TInt aIndex) const
    {
    const CListBoxView::CSelectionIndexArray* selections =
        ListBox().View()->SelectionIndexes();
    return (selections ? Find(*selections,aIndex)!=KErrNotFound : EFalse);
    }

EXPORT_C TInt CPbkContactViewListControl::NextUnmarkedIndexFromFocus() const
    {
    const TInt focus = ListBox().CurrentItemIndex();
    TInt index = focus;
    const CListBoxView::CSelectionIndexArray* selections =
        ListBox().SelectionIndexes();
    if (selections && selections->Count() > 0)
        {
        const TInt count = iListBox->Model()->NumberOfItems();
        for (index = focus; index < count; ++index)
            {
            if (Find(*selections,index) == KErrNotFound)
                {
                return index;
                }
            }
        for (index = focus; index >= 0; --index)
            {
            if (Find(*selections,index) == KErrNotFound)
                {
                return index;
                }
            }
        }
    return index;
    }

EXPORT_C void CPbkContactViewListControl::MarkItemL
        (TContactItemId aContactId,
        TBool aMark)
    {
    const TInt index = FindContactIdL(aContactId);
    if (index >= 0)
        {
        if (aMark)
            {
            ListBox().View()->SelectItemL(index);
            }
        else
            {
            ListBox().View()->DeselectItem(index);
            }
        }
    }

EXPORT_C void CPbkContactViewListControl::ClearMarks()
    {
    if (ItemsMarked())
        {
        CListBoxView& listBoxView = *ListBox().View();
        listBoxView.SetDisableRedraw(ETrue);
        ListBox().ClearSelection();
        listBoxView.SetDisableRedraw(EFalse);
        Redraw();
        }
    }

EXPORT_C CPbkViewState* CPbkContactViewListControl::GetStateL
        (TBool aSaveMarks/*=ETrue*/) const
    {
    CPbkViewState* state = GetStateLC(aSaveMarks);
    CleanupStack::Pop();  // state
    return state;
    }

EXPORT_C CPbkViewState* CPbkContactViewListControl::GetStateLC
        (TBool aSaveMarks/*=ETrue*/) const
    {
    // Create a state object
    CPbkViewState* state = CPbkViewState::NewLC();

    // Init the state object
    if (NumberOfItems() > 0)
        {
        TInt index;
        if ((index = ListBox().TopItemIndex()) >= 0)
            {
            state->SetTopContactId(ContactIdAtL(index));
            }
        if ((index = ListBox().CurrentItemIndex()) >= 0)
            {
            state->SetFocusedContactId(ContactIdAtL(index));
            }
        if (aSaveMarks && ItemsMarked())
            {
            state->SetMarkedContactIds(CContactIdArray::NewL(&MarkedItemsL()));
            }
        }

    // Return the state object
    return state;
    }

EXPORT_C void CPbkContactViewListControl::RestoreStateL(const CPbkViewState* aState)
    {
    PBK_DEBUG_PRINT(
        PBK_DEBUG_STRING("CPbkContactViewListControl::RestoreStateL(0x%x,0x%x)"),
        this, aState);

    if (!aState || !iStateFlags.IsSet(EReady))
        {
        return;
        }

    CEikColumnListBox& listBox = ListBox();
    TBool redraw = EFalse;
    if (aState->Flags() & CPbkViewState::EInitialized)
        {
        // Find box is recreated because default input language needs to
        // be reset here if Always on feature is enabled for Phonebook
        delete iFindBox;
        iFindBox=NULL;

        // Do not enable the find box if there does not exist any contacts
        if (iBaseView->CountL() > 0)
            {
            EnableFindBoxL();
            }

        listBox.Reset();
        redraw = ETrue;
        }
    else
        {
        if (iBaseView->CountL() > 0)
            {
            if (aState->Flags() & CPbkViewState::EFocusFirst)
                {
                listBox.SetTopItemIndex(0);
                }
            else if (aState->Flags() & CPbkViewState::EFocusLast)
                {
                listBox.SetCurrentItemIndex(listBox.Model()->NumberOfItems());
                }
            else
                {
                // Restore top item
                if (aState->TopContactId() != KNullContactId)
                    {
                    const TInt index = FindContactIdL(aState->TopContactId());
                    if (index >= 0)
                        {
                        const TInt prevIndex = listBox.TopItemIndex();
                        listBox.SetTopItemIndex(index);
                        FixTopItemIndex();
                        if (index != prevIndex)
                            {
                            redraw = ETrue;
                            }
                        }
                    }

                // Restore focus
                if (aState->FocusedContactId() != KNullContactId)
                    {
                    const TInt index = FindContactIdL(aState->FocusedContactId());
                    if (index >= 0)
                        {
                        const TInt prevIndex = listBox.CurrentItemIndex();
                        listBox.SetCurrentItemIndex(index);
                        if (index != prevIndex)
                            {
                            redraw = ETrue;
                            }
                        }
                    }

                }
            // Restore selections
            const CContactIdArray* markedContactIds = aState->MarkedContactIds();
            if (RestoreMarkedItemsL(markedContactIds))
                {
                redraw = ETrue;
                }
            }
        }

    if (redraw)
        {
        Redraw();
        iListBox->UpdateScrollBarsL();
        HandleFocusChange();
        }
    }

void CPbkContactViewListControl::ShowThumbnail(TContactItemId aContactId)
    {
    if (!iThumbnailPopup && iEngine)
        {
        TRAP_IGNORE(iThumbnailPopup = CPbkThumbnailPopup::NewL( *iEngine ));
        }

    if (iThumbnailPopup && aContactId!=KNullContactId  && IsReadyToDraw() &&
        !iStateFlags.IsSet(EBlank))
        {
        iThumbnailPopup->Load(aContactId, iListBox);
        }
    else if (iThumbnailPopup)
        {
        iThumbnailPopup->CancelLoading();
        }
    }

EXPORT_C void CPbkContactViewListControl::ShowThumbnailL()
    {
    if (!iThumbnailPopup && iEngine)
        {
        iThumbnailPopup = CPbkThumbnailPopup::NewL( *iEngine );
        }

    if (iLastFocusId!=KNullContactId  && IsReadyToDraw() &&
        !iStateFlags.IsSet(EBlank))
        {
        iThumbnailPopup->Load(iLastFocusId, iListBox);
        }
    else
        {
        iThumbnailPopup->CancelLoading();
        }
    }

EXPORT_C void CPbkContactViewListControl::HideThumbnail()
    {
    if (iThumbnailPopup)
        {
        iThumbnailPopup->CancelLoading();
        }
    }

EXPORT_C void CPbkContactViewListControl::AddObserverL
        (MPbkContactViewListControlObserver& aObserver)
    {
    User::LeaveIfError(iObservers.Append(&aObserver));
    }

EXPORT_C void CPbkContactViewListControl::RemoveObserver
        (MPbkContactViewListControlObserver& aObserver)
    {
    const TInt index = iObservers.Find(&aObserver);
    if (index >= 0)
        {
        iObservers.Remove(index);
        }
    }

EXPORT_C TBool CPbkContactViewListControl::IsReady() const
    {
    return (iStateFlags.IsSet(EReady));
    }

void CPbkContactViewListControl::SetDisableRedraw(TBool aDisableRedraw)
    {
    ListBox().View()->SetDisableRedraw(aDisableRedraw);
    }

void CPbkContactViewListControl::DisableRedrawEnablePushL()
    {
    CListBoxView& listBoxView = *ListBox().View();
    listBoxView.SetDisableRedraw(ETrue);
    CleanupStack::PushL(TCleanupEnableListBoxViewRedraw(listBoxView));
    }

EXPORT_C void CPbkContactViewListControl::SetBlank(TBool aBlank)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::SetBlank(0x%x,%d)"),this,aBlank);

    if ((iStateFlags.IsSet(EBlank) && aBlank) ||
        (!iStateFlags.IsSet(EBlank) && !aBlank))
        {
        // No change in state
        return;
        }

    // Set the EBlank flag
    iStateFlags.Assign(EBlank,aBlank);

    // Hide/unhide component controls
    MakeComponentsVisible(!aBlank);
    Redraw();
    }

EXPORT_C void CPbkContactViewListControl::SetEntryLoader
        (MPbkContactEntryLoader& aContactEntryLoader)
    {
    Model().SetEntryLoader(aContactEntryLoader);
    }


void CPbkContactViewListControl::SetSelectionAccepter(MPbkFetchDlgSelection* aAccepter)
    {
    iSelectionAccepter = aAccepter;
    }

TInt CPbkContactViewListControl::NumberOfItems() const
    {
    return ListBox().Model()->NumberOfItems();
    }

TBool CPbkContactViewListControl::ItemsMarked() const
    {
    const CListBoxView::CSelectionIndexArray* selections =
        ListBox().View()->SelectionIndexes();
    return (selections && selections->Count()>0);
    }

const CContactIdArray& CPbkContactViewListControl::MarkedItemsL() const
    {
    if (!iMarkedItemsArray)
        {
        iMarkedItemsArray = CContactIdArray::NewL();
        }

    // Remove old contents of array (use Remove instead of Reset to keep the
    // array buffer)
    iMarkedItemsArray->Remove(0,iMarkedItemsArray->Count());

    // Get the list boxes marked items index array
    const CListBoxView::CSelectionIndexArray* selArray =
        ListBox().SelectionIndexes();

    if (selArray && selArray->Count() > 0)
        {
        // Initialize the marked contact id array using selArray
        CContactViewBase::TVirtualFunction1Params params
            (selArray, iMarkedItemsArray);

        iView->CContactViewBase_Reserved_1
            (CContactViewBase::ECContactViewBaseVirtualFunction1, &params);
        }
    else
        {
        // No items marked, add the focused contact to the array
        iMarkedItemsArray->AddL(FocusedContactIdL());
        }

    return *iMarkedItemsArray;
    }

TContactItemId CPbkContactViewListControl::FocusedContactIdL() const
    {
    TContactItemId retId(KNullContactId);

    const TInt focusIndex = ListBox().CurrentItemIndex();
    if (focusIndex >= 0)
        {
        retId = iView->AtL(focusIndex);
        }
    return retId;
    }

const TPbkContactItemField* CPbkContactViewListControl::FocusedField() const
    {
    // This control does not support field level focus
    return NULL;
    }

MObjectProvider* CPbkContactViewListControl::ObjectProvider()
    {
    return this;
    }

TKeyResponse CPbkContactViewListControl::OfferKeyEventL
        (const TKeyEvent& aKeyEvent,TEventCode aType)
    {
    if (!iStateFlags.IsSet(EReady) || iStateFlags.IsSet(EBlank))
        {
        // Don't handle any keys when not ready or blanked
        return EKeyWasNotConsumed;
        }

    TKeyResponse result = EKeyWasNotConsumed;

    if (iFindBox)
        {
        // Find box is active, offer key first to it
        result = iFindBox->OfferKeyEventL(aKeyEvent, aType);
        }

    if (result == EKeyWasNotConsumed && iListBox)
        {
        // Find box didn't consume the event -> offer it to the list box
        const TInt focusIndex = iListBox->CurrentItemIndex();
        const TBool markedBefore = ItemMarked(focusIndex);

        // If selection key was pressed and the focused contact was earlier
        // unselected, then first check from selection accepter if this item
        // can be selected
        // Also do the acception-check if there are no selected items and
        // OK-softkey was pressed (causing focused item to be selected and
        // closing the dialog
        TBool checkSelection = EFalse;
        const CListBoxView::CSelectionIndexArray* selArray =
                ListBox().SelectionIndexes();
        
        
        const TInt selectionKeyPressed(aKeyEvent.iCode == EKeyDevice3);
        
        // AVKON may create EKeyApplicationF key event 
        // when list item is selected.
        const TInt avkonSelectionKeyNotification(
                        aKeyEvent.iCode == EKeyApplicationF);
                        
        const TBool noSelectionMade(!selArray||selArray->Count() == 0 );
        
        const TBool validNewFocusedItem(
                        !markedBefore && focusIndex >= KErrNone);

        if ((selectionKeyPressed || (avkonSelectionKeyNotification && noSelectionMade))
            && validNewFocusedItem)
            {
            checkSelection = ETrue;
            }
        
        const CListBoxView::CSelectionIndexArray* selections =
            ListBox().SelectionIndexes();
        TInt selCount = selections ? selections->Count() : 0;
        
        if (!checkSelection || !iSelectionAccepter ||
            focusIndex != KErrNotFound &&
            iSelectionAccepter->ContactSelectionAcceptedL(
                ContactIdAtL(focusIndex), selCount))
            {
            result = iListBox->OfferKeyEventL(aKeyEvent, aType);

            if (result == EKeyWasConsumed)
                {
                const TBool markedAfter = ItemMarked(focusIndex);
                if (markedAfter != markedBefore)
                    {
                    TPbkContactViewListControlEvent
                        event(TPbkContactViewListControlEvent::EContactSelected);
                    event.iInt = focusIndex;
                    event.iContactId = ContactIdAtL(focusIndex);
                    if (!markedAfter)
                        {
                        event.iEventType =
                            TPbkContactViewListControlEvent::EContactUnselected;
                        }
                    SendEventToObserversL(event);
                    }                
                }
            }
        else
            {
            result = EKeyWasConsumed;
            }
        }
    return result;
    }

void CPbkContactViewListControl::HandlePointerEventL(
        const TPointerEvent& aPointerEvent )
    {
    if ( AknLayoutUtils::PenEnabled() )
        {
        if ( iFindBox )
            {
            iFindBox->HandlePointerEventL( aPointerEvent );
            }

        if ( iListBox )
            {
            switch ( aPointerEvent.iType )
                {
                case TPointerEvent::EButton1Down:
                    {
                    iPrevIndex = iListBox->CurrentItemIndex();
                    iListBox->HandlePointerEventL( aPointerEvent );
                    break;
                    }
                case TPointerEvent::EButton1Up:
                    {
                    TInt focusIndex;
                    TBool focusableContactPointed =
                        iListBox->View()->XYPosToItemIndex(
                            aPointerEvent.iPosition, focusIndex );
                    if (!focusableContactPointed || focusIndex < 0)
                        {
                        // Nothing special to do when tapping empty space
                        iListBox->HandlePointerEventL( aPointerEvent );
                        }
                    else
                        {
                        // Send contact tap events                        
                        TPbkContactViewListControlEvent event(
                            TPbkContactViewListControlEvent::EContactTapped );
                        event.iInt = focusIndex;
                        event.iContactId = ContactIdAtL( focusIndex );
                        if ( iPrevIndex == iListBox->CurrentItemIndex() )
                            {
                            event.iEventType =
                                TPbkContactViewListControlEvent::EContactDoubleTapped;
                            }
                        SendEventToObserversL( event );

                        // Do markings
                        const TBool markedBefore = ItemMarked(focusIndex);
                        const CListBoxView::CSelectionIndexArray* selections =
                            ListBox().SelectionIndexes();
                        TInt selCount = selections ? selections->Count() : 0;

                        if ( markedBefore ||         // can unmark always
                             !iSelectionAccepter ||  // no select restrictions
                             iSelectionAccepter->ContactSelectionAcceptedL(
                                ContactIdAtL(focusIndex), selCount) )
                            {
                            // Handle the pointer event to list box to fix the problem that items in multiple
                            // fetch dialog can't be select with touch screen.
                            iListBox->HandlePointerEventL( aPointerEvent );
                            
                            const TBool markedAfter = ItemMarked( focusIndex );
                            if ( markedAfter != markedBefore )
                                {
                                event.iEventType =
                                    TPbkContactViewListControlEvent::EContactSelected;
                                event.iInt = focusIndex;
                                event.iContactId = ContactIdAtL(focusIndex);
                                if (!markedAfter)
                                    {
                                    event.iEventType =
                                        TPbkContactViewListControlEvent::EContactUnselected;
                                    }
                                SendEventToObserversL(event);
                                }
                            }
                        }
                    break;
                    }
                default:
                    {
                    iListBox->HandlePointerEventL( aPointerEvent );
                    }
                }
            }
        }
    }

void CPbkContactViewListControl::MakeVisible(TBool aVisible)
    {
    PBK_DEBUG_PRINT(
        PBK_DEBUG_STRING("CPbkContactViewListControl::MakeVisible(0x%x,%d)"),
        this, aVisible);

    CCoeControl::MakeVisible(aVisible);
    MakeComponentsVisible(aVisible);
    }

void CPbkContactViewListControl::UpdateContact(TContactItemId aContactId)
    {
    TInt listBoxRow = -1;
    TRAPD(err, listBoxRow = FindContactIdL(aContactId) );
    if (err)
        {
        iEikonEnv->NotifyIdleErrorWhileRedrawing(err);
        }
    if (listBoxRow >= 0)
        {
        // if visible
        if (listBoxRow >= ListBox().TopItemIndex() &&
            listBoxRow <= ListBox().BottomItemIndex() )
            {
            ListBox().DrawItem(listBoxRow);
            }
        }
    }

TInt CPbkContactViewListControl::CountComponentControls() const
    {
    TInt controls = 0;
    if (iListBox)
        {
        ++controls;
        }
    if (iFindBox)
        {
        ++controls;
        }
    return controls;
    }

CCoeControl* CPbkContactViewListControl::ComponentControl(TInt aIndex) const
    {
    switch (aIndex)
        {
        case 0:
            {
            __ASSERT_DEBUG(iListBox, Panic(EPanicLogic_ComponentControl));
            return iListBox;
            }
        case 1:
            {
            __ASSERT_DEBUG(iFindBox, Panic(EPanicLogic_ComponentControl));
            return iFindBox;
            }
        default:
            {
            // Illegal state
            __ASSERT_DEBUG(EFalse, Panic(EPanicLogic_ComponentControl));
            return NULL;
            }
        }
    }

void CPbkContactViewListControl::FocusChanged(TDrawNow aDrawNow)
    {
    const TBool focused = IsFocused();
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::FocusChanged(0x%x,%d),focused=%d"),
        this, aDrawNow, focused ? ETrue : EFalse);

    if (focused)
        {
        ShowThumbnail(iLastFocusId);
        }
    else
        {
        HideThumbnail();
        }

    const TInt count = CountComponentControls();
    for (TInt i=0; i < count; ++i)
        {
        CCoeControl* componentControl = ComponentControl(i);
        // Don't try to focus non-focusing controls
        if (!componentControl->IsNonFocusing())
            {
            componentControl->SetFocus(focused,aDrawNow);
            }
        }
    }

void CPbkContactViewListControl::SizeChanged()
    {
    const TRect rect(Rect());
    PBK_DEBUG_PRINT(
        PBK_DEBUG_STRING("CPbkContactViewListControl::SizeChanged(0x%x), rect=(%d,%d,%d,%d)"),
        this, rect.iTl.iX, rect.iTl.iY, rect.iBr.iX, rect.iBr.iY);

    if (iListBox && iFindBox)
        {
        AknLayoutUtils::LayoutControl(iListBox, rect,
            AKN_LAYOUT_WINDOW_list_gen_pane(1));
        AknLayoutUtils::LayoutControl(iFindBox, rect,
            AKN_LAYOUT_WINDOW_find_pane);
        if (iFindBox->IsVisible() && iListBox->IsVisible())
            {
            // The correct line position to use is 2, which corresponds
            // EABColumn in Avkon (not a public enumeration,
            // hence hard-coding used here)
            const TInt KSeparatorLinePos = 2;
            iFindBox->SetLinePos(KSeparatorLinePos);
            }
        }
    else if (iListBox)
        {
        AknLayoutUtils::LayoutControl(iListBox, rect,
            AKN_LAYOUT_WINDOW_list_gen_pane(0));
        }
    }

void CPbkContactViewListControl::Draw(const TRect& aRect) const
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
        "CPbkContactViewListControl::Draw(0x%x, TRect(%d,%d,%d,%d)), iStateFlags=0x%x"),
        this, aRect.iTl.iX, aRect.iTl.iY, aRect.iBr.iX, aRect.iBr.iY, iStateFlags.iFlags);

    if (!iStateFlags.IsSet(EReady) || iStateFlags.IsSet(EBlank))
        {
        // If control is not ready or blanked draw a blank background
        CWindowGc& gc = SystemGc();
        MAknsSkinInstance* skin = AknsUtils::SkinInstance();
        MAknsControlContext* cc = AknsDrawUtils::ControlContext(iListBox);
        if ( !AknsDrawUtils::Background(skin, cc, iListBox, gc, aRect) )
            {
            // blank background if no skin present
            gc.SetPenStyle(CGraphicsContext::ENullPen);
            gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
            gc.DrawRect(aRect);
            }
        }
    }

void CPbkContactViewListControl::HandleControlEventL
        (CCoeControl* aControl,
        TCoeEvent aEventType)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::HandleControlEventL(0x%x,0x%x,%d)"),
        this, aControl, aEventType);

    if (aEventType == EEventStateChanged)
        {
        if (aControl == iListBox)
            {
            HandleFocusChange();
            // Forward listbox state change events to this control's
            // observers
            ReportEventL(MCoeControlObserver::EEventStateChanged);
            }
        else if (aControl == iFindBox)
            {
            UpdateFindResultL();
            }
        }
    }

void CPbkContactViewListControl::HandleContactViewEvent
        (const CContactViewBase& aView,const TContactViewEvent& aEvent)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::HandleContactViewEvent(0x%x,0x%x,%d)"),
        this, &aView, aEvent.iEventType);

    if (&aView == iView)
        {
        TRAPD(err, DoHandleContactViewEventL(aView,aEvent));
        if (err != KErrNone)
            {
            iEikonEnv->HandleError(err);
            }
        }
    }

/**
 * Called from HandleContactViewEvent().
 */
void CPbkContactViewListControl::DoHandleContactViewEventL
        (const CContactViewBase& /*aView*/,const TContactViewEvent& aEvent)
    {
    switch (aEvent.iEventType)
        {
        case TContactViewEvent::EReady:
            {
            iStateFlags.Set(EReady);
            MakeComponentsVisible(IsVisible());
            iListBox->Reset();
            UpdateFindBoxL();
            iListBox->UpdateScrollBarsL();
            Redraw();
            HandleFocusChange();
            SendEventToObserversL(TPbkContactViewListControlEvent::EReady);
            break;
            }

        case TContactViewEvent::ESortOrderChanged:
            {
            Model().FlushCache();
            Model().RefreshSortOrderL();
            iStateFlags.Set(EReady);
            MakeComponentsVisible(IsVisible());
            iListBox->Reset();
            UpdateFindBoxL();
            iListBox->UpdateScrollBarsL();
            Redraw();
            HandleFocusChange();
            // Send EReady event
            SendEventToObserversL(TPbkContactViewListControlEvent::EReady);
            break;
            }

        case TContactViewEvent::EItemAdded:
            {
            HandleItemAdditionL(aEvent.iInt);
            TPbkContactViewListControlEvent event
                (TPbkContactViewListControlEvent::EItemAdded);
            event.iInt = aEvent.iInt;
            event.iContactId = aEvent.iContactId;
            SendEventToObserversL(event);
            break;
            }

        case TContactViewEvent::EItemRemoved:
            {
            Model().PurgeEntry(aEvent.iContactId);
            HandleItemRemovalL(aEvent.iInt);
            TPbkContactViewListControlEvent event
                (TPbkContactViewListControlEvent::EItemRemoved);
            event.iInt = aEvent.iInt;
            event.iContactId = aEvent.iContactId;
            SendEventToObserversL(event);
            break;
            }

        case TContactViewEvent::EUnavailable:
            {
            iStateFlags.Clear(EReady);
            MakeComponentsVisible(EFalse);
            SendEventToObserversL
                (TPbkContactViewListControlEvent::EUnavailable);
            break;
            }

        case TContactViewEvent::ESortError:  //FALLTHROUGH
        case TContactViewEvent::EServerError:  //FALLTHROUGH
        case TContactViewEvent::EIndexingError:
            {
            iStateFlags.Clear(EReady);
            MakeComponentsVisible(EFalse);
            iEikonEnv->HandleError(aEvent.iInt);
            iListBox->Reset();
            DisableFindBoxL();
            TPbkContactViewListControlEvent event
                (TPbkContactViewListControlEvent::EUnavailable);
            event.iInt = aEvent.iInt;
            SendEventToObserversL(event);
            break;
            }

        default:
            {
            break;
            }
        }
    }

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

void CPbkContactViewListControl::ConstructL
        (CPbkContactEngine& aEngine,
        CContactViewBase& aView,
        TInt aResourceId,
        const CCoeControl* aParent)
    {
    if (aParent)
        {
        SetContainerWindowL(*aParent);
        }
    else
        {
        CreateWindowL();
        }
    ConstructFromResourceL(aResourceId);
    ConstructL(aEngine, aView);
    CreateControlExtensionL(aEngine);
    }

void CPbkContactViewListControl::ConstructFromResourceL
        (TInt aResourceId)
    {
    TResourceReader resReader;
    iCoeEnv->CreateResourceReaderLC(resReader,aResourceId);
    ConstructFromResourceL(resReader);
    CleanupStack::PopAndDestroy();  // resReader
    }

void CPbkContactViewListControl::FixTopItemIndex()
    {
    CEikListBox& listBox = ListBox();
    TInt index = listBox.TopItemIndex();
    const TInt height = listBox.View()->NumberOfItemsThatFitInRect
        (iListBox->View()->ViewRect());
    const TInt numItems = listBox.Model()->NumberOfItems();
    if (index + height > numItems)
        {
        index += numItems - (height + index);
        if (index < 0)
            {
            index = 0;
            }
        if (index != listBox.TopItemIndex())
            {
            listBox.SetTopItemIndex(index);
            }
        }
    }

/**
 * Redraws this control using DrawDeferred().
 */
void CPbkContactViewListControl::Redraw()
    {
    if (iStateFlags.IsSet(EReady) && !iStateFlags.IsSet(EBlank))
        {
        DrawDeferred();
        }
    }

void CPbkContactViewListControl::MakeComponentsVisible(TBool aVisible)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::MakeComponentsVisible(0x%x,%d)"),
        this, aVisible);

    if (aVisible && (!iStateFlags.IsSet(EReady) || iStateFlags.IsSet(EBlank)))
        {
        // Never make components visible if this control is not ready or in
        // blank state
        return;
        }

    if (iListBox)
        {
        iListBox->MakeVisible(aVisible);
        }

    if (iFindBox)
        {
        iFindBox->SetFocus(aVisible);
        iFindBox->MakeVisible(aVisible);
        }

    // Hide/show thumbnail
    if (aVisible)
        {
        ShowThumbnail(iLastFocusId);
        }
    else
        {
        HideThumbnail();
        }
    }

void CPbkContactViewListControl::HandleItemAdditionL(TInt aIndex)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::HandleItemAdditionL(0x%x, %d)"),
        this, aIndex);

    UpdateFindBoxL();

    // Update listbox
    iChangedIndexes[0] = aIndex;
    iListBox->HandleItemAdditionL(iChangedIndexes);

    // Maintain focus
    const TInt index = iView->FindL(iLastFocusId);
    if ( index >= 0 &&
         index < iListBox->Model()->NumberOfItems() &&
         index != iListBox->CurrentItemIndex() )
        {
        iListBox->SetCurrentItemIndex(index);
        }
    Redraw();
    HandleFocusChange();
    }

void CPbkContactViewListControl::HandleItemRemovalL(TInt aIndex)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactViewListControl::HandleItemRemovalL(0x%x, %d)"),
        this, aIndex);

    // Deselect item
    CListBoxView& listBoxView = *ListBox().View();
    listBoxView.SetDisableRedraw(ETrue);
    listBoxView.DeselectItem(aIndex);
    listBoxView.SetDisableRedraw(EFalse);

    UpdateFindBoxL();

    // Update listbox
    TInt focusIndex = iListBox->CurrentItemIndex();
    TInt topIndex = iListBox->TopItemIndex();
    iChangedIndexes[0] = aIndex;
    iListBox->HandleItemRemovalL(iChangedIndexes);

    // Maintain focus
    const TInt lastIndex = iListBox->Model()->NumberOfItems() - 1;
    if (lastIndex == KNullContactId)
        {
        iListBox->Reset();
        }
    else
        {
        if (aIndex < focusIndex)
            {
            --focusIndex;
            }
        if (focusIndex > lastIndex || focusIndex < 0)
            {
            focusIndex = lastIndex;
            }
        const TInt numVisibleItems =
            iListBox->View()->NumberOfItemsThatFitInRect
                (iListBox->View()->ViewRect());
        if (topIndex + numVisibleItems > lastIndex)
            {
            topIndex = Max(lastIndex - numVisibleItems + 1, 0);
            }
        if (topIndex >= 0)
            {
            iListBox->SetTopItemIndex(topIndex);
            }
        if (focusIndex >= 0)
            {
            iListBox->SetCurrentItemIndex(focusIndex);
            }
        }
    Redraw();
    HandleFocusChange();
    }

/**
 * Hides or displays the find box depending on control state.
 */
void CPbkContactViewListControl::UpdateFindBoxL()
    {
    if ((iResourceData.iFlags & KPbkContactViewListControlFindBox) &&
        iBaseView->CountL() > 0)
        {
        EnableFindBoxL();
        }
    else
        {
        DisableFindBoxL();
        }
    }

void CPbkContactViewListControl::EnableFindBoxL()
    {
    if (!iFindBox)
        {
        // Create a find box
        iFindBox = CAknSearchField::NewL
            (*this, CAknSearchField::ESearch, NULL,
            CPbkConstants::SearchFieldLength());

        iFindBox->SetObserver(this);
        iFindBox->SetFocus(ETrue);
        iFindBox->ResetL();

        // Set default input mode to Katakana if current UI language is Japanese
        if (FeatureManager::FeatureSupported(KFeatureIdJapanese) &&
            (User::Language() == ELangJapanese))
            {
            CEikEdwin& findEditor = iFindBox->Editor();
            findEditor.SetAknEditorInputMode(EAknEditorKatakanaInputMode);
            }

        // Inform list box that find is visible again
        static_cast<CAknColumnListBoxView*>(iListBox->View())
            ->SetFindEmptyListState(ETrue);
        SetFindEmptyTextL();

        // Relayout view
        SizeChanged();
        UpdateFindResultL();
        }
    __ASSERT_DEBUG(iFindBox, Panic(EPanicPostCond_EnableFindBoxL));
    }

void CPbkContactViewListControl::DisableFindBoxL()
    {
    if (iFindBox)
        {
        // Important to set the find box non-focusing before
        // deleting it, otherwise the focus changes triggered
        // by the removal of the control from stack will focus
        // the find box which is under deletion
        iFindBox->SetNonFocusing();
        delete iFindBox;
        iFindBox = NULL;
        // Inform list box that find is hidden
        static_cast<CAknColumnListBoxView*>(iListBox->View())
            ->SetFindEmptyListState(EFalse);
        RemoveFindEmptyTextL();

        // Relayout view
        SizeChanged();
        UpdateFindResultL();
        }
    __ASSERT_DEBUG(!iFindBox, Panic(EPanicPostCond_DisableFindBoxL));
    }

/**
 * Updates find result.
 * @return ETrue if there was a change in find result set.
 */
TBool CPbkContactViewListControl::UpdateFindResultL()
    {
    TBool result = EFalse;
    const CContactIdArray* markedItems = NULL;
    const TInt countBefore = iView->CountL();

    if (ItemsMarked())
        {
        markedItems = &MarkedItemsL();
        }
    TRAPD( err,
        result = iView->SetFindTextL(FindTextL(), markedItems) );
    if (err != KErrNone)
        {
        iEikonEnv->HandleError(err);
        }

    const TInt countAfter = iView->CountL();

    if (err)
        {
        // Error (possibly OOM), only redraw what remains in the screen
        SizeChanged();
        Redraw();
        }
    else if (result)
        {
        iListBox->DrawDeferred();
        if (countAfter > countBefore)
            {
            iListBox->HandleItemAdditionL();
            }
        else if (countAfter < countBefore)
            {
            iListBox->HandleItemRemovalL();
            }

        if (markedItems)
            {
            // Restore item marks
            CListBoxView& listBoxView = *iListBox->View();
            listBoxView.SetDisableRedraw(ETrue);
            CleanupStack::PushL(TCleanupEnableListBoxViewRedraw(listBoxView));
            listBoxView.ClearSelection();
            const TInt count = markedItems->Count();
            #ifdef PBK_BENCHMARK_FIND
            TRunningTimes<TInt> timer;
            #endif
            for (TInt i=0; i < count; ++i)
                {
                const TContactItemId contactId = (*markedItems)[i];
                #ifdef PBK_BENCHMARK_FIND
                timer.Start();
                #endif
                const TInt index =iView->FindL(contactId);
                #ifdef PBK_BENCHMARK_FIND
                timer.Stop();
                #endif
                listBoxView.SelectItemL(index);
                }
            #ifdef PBK_BENCHMARK_FIND
            RDebug::Print(_L(
                "CPbkContactViewListControl::UpdateFindResultL FindL calls %d tot %d"),
                timer.Count(), timer.Total());
            #endif
            CleanupStack::PopAndDestroy(); //TCleanupEnableListBoxViewRedraw
            }

        // This event is send to observers,
        // if contact set of the control is changed
        TPbkContactViewListControlEvent
            event(TPbkContactViewListControlEvent::EContactSetChanged);
        SendEventToObserversL(event);
        }

    TInt focusIndex = iView->IndexOfFirstFindMatchL();
    if (focusIndex < 0)
        {
        if (countAfter > 0)
            {
            focusIndex = 0;
            }
        }
    if (focusIndex >= 0 && focusIndex != iListBox->CurrentItemIndex())
        {
        iListBox->SetCurrentItemIndexAndDraw(focusIndex);
        }
    HandleFocusChange();

    return result;
    }

void CPbkContactViewListControl::SetFindEmptyTextL()
    {
    if (!iOriginalEmptyText)
        {
        iOriginalEmptyText = iListBox->View()->EmptyListText()->AllocL();
        }
    iListBox->View()->SetListEmptyTextL(*iResourceData.iFindEmptyText);
    }

void CPbkContactViewListControl::RemoveFindEmptyTextL()
    {
    if (iOriginalEmptyText)
        {
        iListBox->View()->SetListEmptyTextL(*iOriginalEmptyText);
        }
    }

void CPbkContactViewListControl::HandleFocusChange()
    {
    TRAP_IGNORE(HandleFocusChangeL());
    }

void CPbkContactViewListControl::HandleFocusChangeL()
    {
    const TInt index = ListBox().CurrentItemIndex();
    TContactItemId newFocusId = KNullContactId;
    if (index >= 0 && index < iView->CountL())
        {
        newFocusId = iView->AtL(index);
        }
    if (newFocusId != iLastFocusId)
        {
        iLastFocusId = newFocusId;
        HideThumbnail();
        ShowThumbnail(newFocusId);
        }
    }

/**
 * Marks specified contacts in the listbox.
 *
 * @param aMarkedContactIds contacts to mark.
 * @return true if any contacts were marked in the list.
 */
TBool CPbkContactViewListControl::RestoreMarkedItemsL
        (const CContactIdArray* aMarkedContactIds)
    {
    CEikListBox& listBox = ListBox();
    TBool result = EFalse;
    DisableRedrawEnablePushL();
    listBox.ClearSelection();
    if (aMarkedContactIds)
        {
        const TInt count = aMarkedContactIds->Count();
        for (TInt i=0; i < count; ++i)
            {
            const TInt index = FindContactIdL((*aMarkedContactIds)[i]);
            if (index >= 0)
                {
                listBox.View()->SelectItemL(index);
                result = ETrue;
                }
            }
        }
    CleanupStack::PopAndDestroy();  // DisableRedrawEnablePushL
    return result;
    }

/**
 * Creates control extension for a model, appends new icons
 * and sets this object as control updator for the extension.
 *
 * @param aEngine provided for control extension
 */
void CPbkContactViewListControl::CreateControlExtensionL
        (CPbkContactEngine& aEngine)
    {
    __ASSERT_DEBUG(&Model(), Panic(EPanicPre_SetControlExtensionL));
    __ASSERT_DEBUG(!iControlExtension, Panic(EPanicPre_SetControlExtensionL));

    CPbkExtGlobals* extGlobal = CPbkExtGlobals::InstanceL();
    extGlobal->PushL();
    iControlExtension = extGlobal->FactoryL().
            CreatePbkUiControlExtensionL(aEngine);
    CleanupStack::PopAndDestroy(extGlobal);

    Model().SetContactUiControlExtension(*iControlExtension);

    TInt arrayInfoId = 0;
    TInt arrayId = 0;
    iControlExtension->IconArrayResourceId(arrayInfoId, arrayId);
    if ( arrayInfoId != 0 )
        {
        IconArray(*iListBox)->AppendIconsFromResourceL(arrayInfoId, arrayId);
        }

    iControlExtension->SetContactUiControlUpdate(this);
    }

void CPbkContactViewListControl::HandleResourceChange(TInt aType)
    {
    CPbkContactListControlBase::HandleResourceChange(aType);

    TRAP_IGNORE(DoHandleResourceChangeL(aType));
    }

void CPbkContactViewListControl::RefreshIconArrayL()
    {
    // Control extension may be NULL when skin is changed at run-time
    if (iControlExtension)
        {
        CPbkIconArray* iconArray = IconArray(*iListBox);
        if (iconArray)
            {
            // refresh the main pbk icon array
            iconArray->RefreshL(R_PBK_ICON_INFO_ARRAY);
            // also refresh the control extension icon array
            TInt arrayInfoId = 0;
            TInt arrayId = 0;
            iControlExtension->IconArrayResourceId(arrayInfoId, arrayId);
            if (arrayInfoId != 0)
                {
                iconArray->RefreshL(arrayInfoId);
                }
            }
        }
    }

void CPbkContactViewListControl::DoHandleResourceChangeL(TInt aType)
    {
    if (aType == KAknsMessageSkinChange)
        {
        RefreshIconArrayL();
        }
    else if (aType == KEikDynamicLayoutVariantSwitch)
        {
        RefreshIconArrayL();
        SizeChanged();
        const TBool focused( IsFocused() );
        const TBool nonFocusing( IsNonFocusing() );
        if ( focused && !nonFocusing )
            {
            ShowThumbnailL();
            }
        DrawNow();
        }
    }


// TCleanupEnableListBoxViewRedraw
void TCleanupEnableListBoxViewRedraw::CleanupOp(TAny* aPtr)
    {
    static_cast<CListBoxView*>(aPtr)->SetDisableRedraw(EFalse);
    }

EXPORT_C void CPbkContactViewListControl::DeleteThumbnail()
    {
    delete iThumbnailPopup;
    iThumbnailPopup = NULL;
    }
    
void CPbkContactViewListControl::EnableMSKObserver( TBool aEnable )
    {
    iListBox->EnableMSKObserver(aEnable);
    }
    
TInt CPbkContactViewListControl::ItemCount()
    {
    return Model().MdcaCount(); 
    }

//  End of File