/*
* Copyright (c) 2006-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:  A stack for the base view and filtered views.
*
*/


#include "cpbk2filteredviewstack.h"

// Phonebook 2
#include "cpbk2filteredviewstackelement.h"

// Virtual Phonebook
#include <MVPbkContactViewFiltering.h>
#include <MVPbkContactViewBase.h>
#include <CVPbkContactFindPolicy.h>

// Debugging headers
#include <Pbk2Debug.h>

// System includes
#include <aknsfld.h>

// CONSTANTS
const TInt KSearchLevelArrayGranularity = 2;

/**
 * A helper class for stack element ownership handling.
 */
NONSHARABLE_CLASS( CElementStack ) : public CBase
    {
    public: // Interface

        /**
         * Destructor.
         */
        ~CElementStack();

        /**
         * Returns the number of elements in the stack.
         *
         * @return  Number of elements in the stack.
         */
        TInt Count() const;

        /**
         * Returns an element in given index.
         *
         * @param aIndex    Index.
         * @return  Element at given index.
         */
        MPbk2FilteredViewStackElement& operator[](
                TInt aIndex );

        /**
         * Appends element to the stack.
         *
         * @param aElement  Element to append. Ownership is taken.
         */
        void AppendL(
                MPbk2FilteredViewStackElement* aElement );

        /**
         * Moves elements from given stack to this stack.
         *
         * @param aStack    Elements to append.
         */
        void AppendStackL(
                CElementStack& aStack );

        /**
         * Destroys all element in the stack starting from the top.
         */
        void Reset();

        /**
         * Removes an element from given position. Client must first
         * take the ownership of the element.
         *
         * @param aIndex    Index.
         */
        void Remove(
                TInt aIndex );

        /**
         * Finds the first element under destruction starting from bottom.
         */
        TInt FindFirstElementUnderDestruction();

        /**
         * Reserves memory for given number of items.
         *
         * @param aNumberOfItems    Number of items.
         */
        void ReserveL(
                TInt aNumberOfItems );

        /**
         * Used together with ReserveL. Append doesn't leave if ReserveL has
         * been called earlier.
         *
         * @param aElement  Element to append.
         */
        void Append(
                MPbk2FilteredViewStackElement* aElement );

    private: // Data
        /// Own: Stack of elements
        RPointerArray<MPbk2FilteredViewStackElement> iStack;
    };

/**
 * An internal callback class for handling asynchrnous observer
 * events.
 */
NONSHARABLE_CLASS( CPbk2FilteredViewStack::CCallback ) : public CActive
    {
    public: // Interface
        /**
         * A member function pointer definition for handling AddObserverL
         */
        typedef void(CPbk2FilteredViewStack::* AddObserverL)(
            MVPbkContactViewObserver&, CCallback& );
        /**
         * A member function pointer definition for handling error if
         * AddObserverL -function leaves.
         */
        typedef void(CPbk2FilteredViewStack::* AddObserverError)(
            TInt, CCallback& );

        /**
         * Constructor.
         *
         * @param aViewStack    The parent of this class.
         * @param aObserver     New observer that will given back to
         *                      parent class when the call back completes.
         * @param aAddObserverFuncL     Member function pointer to function
         *                              that handles adding of new observer
         * @param aAddObserverErrorFunc Member function pointer to function
         *                               that handles an error if the
         *                              aAddObserverFuncL leaves.
         */
        CCallback(
                CPbk2FilteredViewStack& aViewStack,
                MVPbkContactViewObserver& aObserver,
                AddObserverL aAddObserverFuncL,
                AddObserverError aAddObserverErrorFunc );

        /**
         * Destructor.
         */
        ~CCallback();

        /**
         * Starts the callback.
         */
        void Execute();

        /**
         * Returns the observer.
         *
         * @return  The observer that were given in construction..
         */
        MVPbkContactViewObserver& Observer();

    private: // From CActive
        void RunL();
        void DoCancel();
        TInt RunError(
                TInt aError );

    private: // Data
        /// Ref: View stack
        CPbk2FilteredViewStack& iViewStack;
        /// Ref: Observer
        MVPbkContactViewObserver& iObserver;
        /// Ref: Add observer function pointer
        AddObserverL iAddObserverFuncL;
        /// Ref: Add observer error function pointer
        AddObserverError iAddObserverErrorFunc;
    };

/// Unnamed namespace for local definitions
namespace {

#ifdef _DEBUG

/**
 * Panic codes for debug build
 */
enum TPanicCode
    {
    EPreCond_ReserveL = 1,
    EPreCond_AppendStackL,
    EInvariantConstructionStackNotIntOrder,
    EInvariantViewStackNotIntOrder,
    EPostCond_Reset
    };

/**
 * A panic function for this class
 */
void Panic(TPanicCode aReason)
    {
    _LIT(KPanicText, "CPbk2FilteredViewStack");
    User::Panic(KPanicText, aReason);
    }

#endif // _DEBUG

// --------------------------------------------------------------------------
// MatchElement
// Matches aElement to an element in aViewStack.
//
// @param aViewStack    Stack of elements.
// @param aElement      The element that is searched for.
// @return  The matched element or NULL.
// --------------------------------------------------------------------------
//
MPbk2FilteredViewStackElement* MatchElement(
        CElementStack& aViewStack,
        MPbk2FilteredViewStackElement& aElement )
    {
    const TInt count = aViewStack.Count();
    for ( TInt i = 0; i < count; ++i )
        {
        if ( aViewStack[i].IsSame( aElement ) )
            {
            return &aViewStack[i];
            }
        }
    return NULL;
    }

// --------------------------------------------------------------------------
// FindElement
// Finds an element for the view.
//
// @param aViewStack    Stack of elements.
// @param aView         The view whose element is searched for.
// @return  The element or NULL.
// --------------------------------------------------------------------------
//
MPbk2FilteredViewStackElement* FindElement(
        CElementStack& aViewStack,
        MVPbkContactViewBase& aView )
    {
    const TInt count = aViewStack.Count();
    for ( TInt i = 0; i < count; ++i )
        {
        if ( aViewStack[i].View() == &aView )
            {
            return &aViewStack[i];
            }
        }
    return NULL;
    }

// --------------------------------------------------------------------------
// FindElement
// Finds an element for the given filter level.
//
// @param aViewStack    Stack of elements.
// @param aFilterLevel  The level of the element to search for.
// @return  The element or NULL.
// --------------------------------------------------------------------------
//
MPbk2FilteredViewStackElement* FindElement(
        CElementStack& aViewStack,
        TInt aFilterLevel )
    {
    const TInt count = aViewStack.Count();
    for ( TInt i = 0; i < count; ++i )
        {
        if ( aViewStack[i].Level() == aFilterLevel )
            {
            return &aViewStack[i];
            }
        }
    return NULL;
    }

// --------------------------------------------------------------------------
// IsStackReady
// The stack is ready if all the elements in the stack are ready.
//
// @param aViewStack    Stack of elements.
// @return  ETrue if the stack is ready.
// --------------------------------------------------------------------------
//
TBool IsStackReady( CElementStack& aViewStack )
    {
    TBool result = ETrue;
    const TInt count = aViewStack.Count();
    for ( TInt i = 0; i < count && result; ++i )
        {
        if ( aViewStack[i].ViewState() !=
             MPbk2FilteredViewStackElement::EReady )
            {
            result = EFalse;
            }
        }
    return result;
    }

// --------------------------------------------------------------------------
// IsStackReadyToUpdate
// The stack is ready to update if all the elements that are not under
// destruction are ready.
//
// @param aViewStack    Stack of elements.
// @return  ETrue if the stack is ready.
// --------------------------------------------------------------------------
//
TBool IsStackReadyToUpdate( CElementStack& aViewStack )
    {
    TBool result = ETrue;
    const TInt count = aViewStack.Count();
    for ( TInt i = 0; i < count && result; ++i )
        {
        if ( !aViewStack[i].UnderDestruction() &&
             aViewStack[i].ViewState() !=
             MPbk2FilteredViewStackElement::EReady )
            {
            result = EFalse;
            }
        }
    return result;
    }

// --------------------------------------------------------------------------
// IsStackUndefined
// The stack is undefined if at least one of the elements in the
// stack is undefined.
//
// @param aViewStack    Stack of elements.
// @return  ETrue if the stack is undefined.
// --------------------------------------------------------------------------
//
TBool IsStackUndefined( CElementStack& aViewStack )
    {
    TBool result = EFalse;
    const TInt count = aViewStack.Count();
    for ( TInt i = 0; i < count && !result; ++i )
        {
        if ( aViewStack[i].ViewState() ==
             MPbk2FilteredViewStackElement::EUndefined )
            {
            result = ETrue;
            }
        }
    return result;
    }

// --------------------------------------------------------------------------
// SendEventToObservers
// Sends events to the array of observes that take MVPbkContactViewBase.
// as a parameter.
//
// @param aView         The parameter for the NotifyFunc.
// @param aObservers    An array of MVPbkContactViewObservers.
// @param aNotifyFunc   A member function pointer of the
//                      MVPbkContactViewObserver.
// --------------------------------------------------------------------------
//
template <class Observer, class NotifyFunc>
void SendEventToObservers( MVPbkContactViewBase& aView,
        RPointerArray<Observer>& aObservers,
        NotifyFunc aNotifyFunc )
    {
    const TInt count = aObservers.Count();
    for (TInt i = count-1; i >= 0 ; --i)
        {
        Observer* observer = aObservers[i];
        (observer->*aNotifyFunc)(aView);
        }
    }

// --------------------------------------------------------------------------
// SendEventToObservers
// Sends events to the array of MVPbkContactViewObserver.
// Used for MVPbkContactViewObserver functions that have two extra parameters
// in addition to MVPbkContactViewBase.
//
// @param aView         The first parameter for the NotifyFunc.
// @param aObservers    An array of MVPbkContactViewObserver.
// @param aNotifyFunc   A member function pointer of the
//                      MVPbkContactViewObserver.
// @param aParam1       The second parameter for the aNotifyFunc.
// @param aParam2       The third parameter for the aNotifyFunc.
// --------------------------------------------------------------------------
//
template <class Observer, class FuncPtr, class ParamType1, class ParamType2>
void SendEventToObservers( MVPbkContactViewBase& aView,
        RPointerArray<Observer>& aObservers,
        FuncPtr aNotifyFunc,
        ParamType1 aParam1, ParamType2& aParam2)
    {
    const TInt count = aObservers.Count();
    for (TInt i = count-1; i >= 0 ; --i)
        {
        Observer* observer = aObservers[i];
        (observer->*aNotifyFunc)(aView, aParam1, aParam2);
        }
    }

} /// namespace

// --------------------------------------------------------------------------
// CElementStack::~CElementStack
// --------------------------------------------------------------------------
//
CElementStack::~CElementStack()
    {
    Reset();
    }

// --------------------------------------------------------------------------
// CElementStack::Count
// --------------------------------------------------------------------------
//
TInt CElementStack::Count() const
    {
    return iStack.Count();
    }

// --------------------------------------------------------------------------
// CElementStack::operator[]
// --------------------------------------------------------------------------
//
MPbk2FilteredViewStackElement& CElementStack::operator[](
        TInt aIndex )
    {
    return *iStack[aIndex];
    }

// --------------------------------------------------------------------------
// CElementStack::AppendL
// --------------------------------------------------------------------------
//
void CElementStack::AppendL( MPbk2FilteredViewStackElement* aElement )
    {
    iStack.AppendL( aElement );
    }

// --------------------------------------------------------------------------
// CElementStack::AppendStackL
// --------------------------------------------------------------------------
//
void CElementStack::AppendStackL( CElementStack& aStack )
    {
    // It's OK if this stack is empty OR
    // aStack is empty OR
    // the level of last element of this stack is 1 smaller than the first
    // element of the aStack
    __ASSERT_DEBUG( iStack.Count() == 0 ||
                    aStack.Count() == 0 ||
                    ( iStack[iStack.Count()-1]->Level() ==
                      aStack[0].Level() - 1 ),
                    Panic( EPreCond_AppendStackL ) );

    while ( aStack.Count() > 0 )
        {
        // Give ownership of the aStack[i] to iStack
        iStack.AppendL( &aStack[0] );
        aStack.Remove( 0 );
        }
    }

// --------------------------------------------------------------------------
// CElementStack::Reset
// --------------------------------------------------------------------------
//
void CElementStack::Reset()
    {
    // Stack's elements need to destroy vice versa,
    // because views in elements are always linked to previous view.
    // Like BaseView<-FindView<-RefineView<-RefineView...
    const TInt count( iStack.Count() );
    for ( TInt i=count-1; i >= 0; --i )
        {
        MPbk2FilteredViewStackElement* element = iStack[i];
        if ( element )
            {
            iStack.Remove(i);
            delete element;
            element = NULL;
            }
        }
    iStack.Close();
    }

// --------------------------------------------------------------------------
// CElementStack::Remove
// --------------------------------------------------------------------------
//
void CElementStack::Remove( TInt aIndex )
    {
    iStack.Remove( aIndex );
    }

// --------------------------------------------------------------------------
// CElementStack::FindFirstElementUnderDestruction
// --------------------------------------------------------------------------
//
TInt CElementStack::FindFirstElementUnderDestruction()
    {
    TInt result = KErrNotFound;
    const TInt count = iStack.Count();
    for ( TInt i = 0; i < count && result == KErrNotFound; ++i )
        {
        if ( iStack[i]->UnderDestruction() )
            {
            result = i;
            }
        }
    return result;
    }

// --------------------------------------------------------------------------
// CElementStack::ReserveL
// --------------------------------------------------------------------------
//
void CElementStack::ReserveL( TInt aNumberOfItems )
    {
    // Function implemented for empty stacks for now
    __ASSERT_DEBUG( Count() == 0, Panic( EPreCond_ReserveL ) );

    for ( TInt i = 0; i < aNumberOfItems; ++i )
        {
        iStack.AppendL( NULL );
        }
    TInt count = Count();
    for ( TInt i = count - 1; i >= 0; --i )
        {
        iStack.Remove( i );
        }
    }

// --------------------------------------------------------------------------
// CElementStack::Append
// --------------------------------------------------------------------------
//
void CElementStack::Append( MPbk2FilteredViewStackElement* aElement )
    {
    /// This function is used together with ReserveL
    iStack.Append( aElement );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::CCallback
// --------------------------------------------------------------------------
//
CPbk2FilteredViewStack::CCallback::CCallback(
        CPbk2FilteredViewStack& aViewStack,
        MVPbkContactViewObserver& aObserver, AddObserverL aAddObserverFuncL,
        AddObserverError aAddObserverErrorFunc ) :
            CActive( EPriorityStandard ),
            iViewStack( aViewStack ),
            iObserver( aObserver ),
            iAddObserverFuncL( aAddObserverFuncL ),
            iAddObserverErrorFunc( aAddObserverErrorFunc )
    {
    CActiveScheduler::Add( this );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::~CCallback
// --------------------------------------------------------------------------
//
CPbk2FilteredViewStack::CCallback::~CCallback()
    {
    Cancel();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::Execute
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::CCallback::Execute()
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, KErrNone );
    SetActive();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::Observer
// --------------------------------------------------------------------------
//
MVPbkContactViewObserver& CPbk2FilteredViewStack::CCallback::Observer()
    {
    return iObserver;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::RunL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::CCallback::RunL()
    {
    (iViewStack.*iAddObserverFuncL)( iObserver, *this );
    // Member data must not be used after the call because this instance
    // will be deleted
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::DoCancel
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::CCallback::DoCancel()
    {
    // Nothing to cancel
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CCallback::RunError
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::CCallback::RunError( TInt aError )
    {
    (iViewStack.*iAddObserverErrorFunc)( aError, *this );
    // Member data must not be used after the call because this instance
    // will be deleted
    return KErrNone;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CPbk2FilteredViewStack
// --------------------------------------------------------------------------
//
inline CPbk2FilteredViewStack::CPbk2FilteredViewStack()
    {
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::~CPbk2FilteredViewStack
// --------------------------------------------------------------------------
//
CPbk2FilteredViewStack::~CPbk2FilteredViewStack()
    {
    delete iConstructionStack;
    delete iViewStack;
    delete iFindPolicy;
    iCallbacks.ResetAndDestroy();
    iViewObservers.Close();
    iStackObservers.Close();
    if ( iBaseViewElement )
        {
        iBaseViewElement->View()->RemoveObserver( *this );
        }
    delete iBaseViewElement;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::NewL
// --------------------------------------------------------------------------
//
CPbk2FilteredViewStack* CPbk2FilteredViewStack::NewL
        ( MVPbkContactViewBase& aBaseView )
    {
    CPbk2FilteredViewStack* self = new ( ELeave ) CPbk2FilteredViewStack;
    CleanupStack::PushL( self );
    self->ConstructL( aBaseView );
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ConstructL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ConstructL( MVPbkContactViewBase& aBaseView )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
        "CPbk2FilteredViewStack::ConstructL"));

    // Create contact find policy
    iFindPolicy = CVPbkContactFindPolicy::NewL();

    iViewStack = new ( ELeave ) CElementStack;
    iConstructionStack = new ( ELeave ) CElementStack;

    // Create element for the base view
    iBaseViewElement = CPbk2FilteredViewStackBaseElement::NewL( aBaseView );
    // Start listening view events
    iBaseViewElement->View()->AddObserverL( *this );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::UpdateFilterL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::UpdateFilterL( const MDesCArray& aFindStrings,
        const MVPbkContactBookmarkCollection* aAlwaysincluded,
        TBool aAlwaysIncludedChanged )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
        "CPbk2FilteredViewStack::UpdateFilterL: topview(0x%x), always=%x"),
        &TopView(), aAlwaysincluded );


    if ( aFindStrings.MdcaCount() == 0 )
        {
        Reset();
        return;
        }

    // Destroy all the views from construction stack. This is a simple
    // logic for the case that UpdateFilterL is called many times with
    // short interval.
    iConstructionStack->Reset();

    // Create or update elements according to given filter
    TBool updateStarted = EFalse;
    const TInt topLevel = CountLengthOfStrings( aFindStrings );

    // Array where to copy search words according to level
    MDesCArray* searchLevelArray = NULL;

    for ( TInt i = 1; i <= topLevel; ++i )
        {
        searchLevelArray = CopySearchWordsByLevelL( aFindStrings, i );
        CleanupStack::PushL( searchLevelArray );

        CPbk2FilteredViewStackElement* newElement =
            CPbk2FilteredViewStackElement::NewL( *iFindPolicy,
                searchLevelArray, aAlwaysincluded );
        CleanupStack::PushL( newElement );
        // Check first that is there already a same element in the stack.
        MPbk2FilteredViewStackElement* element =
            MatchElement( *iViewStack, *newElement );
        if ( !element || aAlwaysIncludedChanged )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
            "CPbk2FilteredViewStack::UpdateFilterL: no match for element"));
            // Then check that is there already an element for the filter level
            element = FindElement( *iViewStack, i );
            if ( element )
                {
                PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
                    "CPbk2FilteredViewStack::UpdateFilterL: update level %d"),
                    i );
                // There is already an element for the level -> Do only update.
                element->UpdateFilterL(
                        *iBaseViewElement->View(),
                        *this,
                        *searchLevelArray,
                        aAlwaysincluded );
                updateStarted = ETrue;
                }
            }

        if ( !element )
            {
            // A new view must be constructed
            iConstructionStack->AppendL( newElement );
            CleanupStack::Pop( newElement );
            }
        else
            {
            // No need to create a new view.
            CleanupStack::PopAndDestroy( newElement );
            }
        CleanupStack::Pop(); //searchLevelArray
        }

    // Set elements that are not part of the new filter
    // to under destruction. These elements are destroyed when the
    // stack is updated.
    for ( TInt i = topLevel; i < iViewStack->Count(); ++i )
        {
        (*iViewStack)[i].SetUnderDestruction();
        }

    // If any of the elements were not updated then we must start creating
    // the first view in construction stack. If views were updated
    // then it will send a view event and rest of the views can be constructed
    // after that.
    if ( !updateStarted )
        {
        if ( iConstructionStack->Count() > 0 )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
                "CPbk2FilteredViewStack::UpdateFilterL:\
                create new view(level=%d)"),
                (*iConstructionStack)[0].Level() );

            (*iConstructionStack)[0].CreateViewL(
                    *iBaseViewElement->View(),
                    TopView(),
                    *this );
            }
        else
            {
            // None of the views were updated and no new views were created.
            // -> update stack if there are views that must be destroyed.
            if ( IsElementsUnderDestruction() )
                {
                UpdateStackL();
                }
            }
        }

    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::Reset
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::Reset()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
        "CPbk2FilteredViewStack::Reset: topview(0x%x), current stack level = %d"),
        &TopView(), TopElement().Level() );

    // This can be reset without problems
    iConstructionStack->Reset();

    // iViewStack needs more careful handling because the top view can
    // change.
    MVPbkContactViewBase* oldTopView = &TopView();
    if ( oldTopView != iBaseViewElement->View() )
        {
        // The top view will be changed. We must set the base view to
        // top view but we cannot reset iViewStack before event has been
        // sent.
        // Set iViewStack to NULL while sending the event so if client
        // uses the stack the top view will be the base view.
        CElementStack* viewStack = iViewStack;
        iViewStack = NULL;
        SendTopViewChangedEvent( *oldTopView );
        // Event has been sent and we can restore the view stack and
        // reset it.
        iViewStack = viewStack;
        iViewStack->Reset();
        }

    __ASSERT_DEBUG( &TopView() == iBaseViewElement->View() &&
                    iConstructionStack &&
                    iConstructionStack->Count() == 0 &&
                    iViewStack &&
                    iViewStack->Count() == 0,
                    Panic( EPostCond_Reset ) );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::BaseView
// --------------------------------------------------------------------------
//
MVPbkContactViewBase& CPbk2FilteredViewStack::BaseView() const
    {
    return *iBaseViewElement->View();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::SetNewBaseViewL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::SetNewBaseViewL(
        MVPbkContactViewBase& aBaseView )
    {
    // Delete all filtered views
    Reset();
    // Remove observering the old base view
    BaseView().RemoveObserver( *this );
    // Create a new base element
    CPbk2FilteredViewStackBaseElement* newBaseElement =
        CPbk2FilteredViewStackBaseElement::NewL( aBaseView );
    delete iBaseViewElement;
    iBaseViewElement = newBaseElement;
    // Start listening to new base view
    BaseView().AddObserverL( *this );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::AddStackObserverL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::AddStackObserverL(
        MPbk2FilteredViewStackObserver& aStackObserver )
    {
    iStackObservers.AppendL( &aStackObserver );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::RemoveStackObserver
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::RemoveStackObserver(
        MPbk2FilteredViewStackObserver& aStackObserver )
    {
    TInt index = iStackObservers.Find( &aStackObserver );
    if ( index != KErrNotFound )
        {
        iStackObservers.Remove( index );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::Level
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::Level() const
    {
    return TopElement().Level();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::Type
// --------------------------------------------------------------------------
//
TVPbkContactViewType CPbk2FilteredViewStack::Type() const
    {
    return TopView().Type();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ChangeSortOrderL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ChangeSortOrderL(
        const MVPbkFieldTypeList& aSortOrder )
    {
    return TopView().ChangeSortOrderL( aSortOrder );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::SortOrder
// --------------------------------------------------------------------------
//
const MVPbkFieldTypeList& CPbk2FilteredViewStack::SortOrder() const
    {
    return TopView().SortOrder();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::RefreshL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::RefreshL()
    {
    return TopView().RefreshL();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactCountL
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::ContactCountL() const
    {
    return TopView().ContactCountL();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactAtL
// --------------------------------------------------------------------------
//
const MVPbkViewContact& CPbk2FilteredViewStack::ContactAtL
        ( TInt aIndex ) const
    {
    return TopView().ContactAtL( aIndex );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CreateLinkLC
// --------------------------------------------------------------------------
//
MVPbkContactLink* CPbk2FilteredViewStack::CreateLinkLC( TInt aIndex ) const
    {
    return TopView().CreateLinkLC( aIndex );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::IndexOfLinkL
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::IndexOfLinkL(
        const MVPbkContactLink& aContactLink ) const
    {
    return TopView().IndexOfLinkL( aContactLink );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::AddObserverL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::AddObserverL
        ( MVPbkContactViewObserver& aObserver )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING(
        "CPbk2FilteredViewStack::AddObserverL"));

    CCallback* callBack =
        new ( ELeave ) CCallback( *this, aObserver,
            &CPbk2FilteredViewStack::DoAddObserverL,
            &CPbk2FilteredViewStack::DoAddObserverError );

    CleanupStack::PushL( callBack );
    iCallbacks.AppendL( callBack );
    CleanupStack::Pop( callBack );
    callBack->Execute();

    // Events are send in reverse order so insert to first position.
    iViewObservers.InsertL( &aObserver, 0 );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::RemoveObserver
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::RemoveObserver(
        MVPbkContactViewObserver& aObserver )
    {
    const TInt count = iCallbacks.Count();
    for ( TInt i = 0; i < count; ++i )
        {
        if ( &iCallbacks[i]->Observer() == &aObserver )
            {
            delete iCallbacks[i];
            iCallbacks.Remove( i );
            break;
            }
        }

    TInt index = iViewObservers.Find( &aObserver );
    if ( index != KErrNotFound )
        {
        iViewObservers.Remove( index );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::MatchContactStore
// --------------------------------------------------------------------------
//
TBool CPbk2FilteredViewStack::MatchContactStore(
        const TDesC& aContactStoreUri ) const
    {
    return TopView().MatchContactStore( aContactStoreUri );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::MatchContactStoreDomain
// --------------------------------------------------------------------------
//
TBool CPbk2FilteredViewStack::MatchContactStoreDomain(
        const TDesC& aContactStoreDomain ) const
    {
    return TopView().MatchContactStoreDomain( aContactStoreDomain );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CreateBookmarkLC
// --------------------------------------------------------------------------
//
MVPbkContactBookmark* CPbk2FilteredViewStack::CreateBookmarkLC(
        TInt aIndex ) const
    {
    return TopView().CreateBookmarkLC( aIndex );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::IndexOfBookmarkL
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::IndexOfBookmarkL(
        const MVPbkContactBookmark& aContactBookmark ) const
    {
    return TopView().IndexOfBookmarkL( aContactBookmark );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ViewFiltering
// --------------------------------------------------------------------------
//
MVPbkContactViewFiltering* CPbk2FilteredViewStack::ViewFiltering()
    {
    // The stack itself doesn't support filtering
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactViewReady
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ContactViewReady( MVPbkContactViewBase& aView )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::ContactViewReady(0x%x), topview(0x%x)"),
        &aView, &TopView() );

    TRAPD( error, DoHandleContactViewReadyL( aView ) );
    if ( error != KErrNone )
        {
        ContactViewError( aView, error, EFalse );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactViewUnavailable
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ContactViewUnavailable(
        MVPbkContactViewBase& aView )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::ContactViewUnavailable(0x%x), topview(0x%x)"),
        &aView, &TopView() );

    // Check first if the aView is the base view.
    if ( iBaseViewElement->View() == &aView )
        {
        iBaseViewElement->SetViewState(
            MPbk2FilteredViewStackElement::EUnavailable );
        }
    else
        {
        // Check if the aView is one of the views in current stack
        MPbk2FilteredViewStackElement* element =
            FindElement( *iViewStack, aView );
        if ( !element )
            {
            element = FindElement( *iConstructionStack, aView );
            }

        if ( element )
            {
            element->SetViewState(
                MPbk2FilteredViewStackElement::EUnavailable );
            }
        }

    if ( &TopView() == &aView )
        {
        // Always forward top view events to clients
        SendEventToObservers( *this, iViewObservers,
            &MVPbkContactViewObserver::ContactViewUnavailable );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactAddedToView
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ContactAddedToView
        ( MVPbkContactViewBase& aView, TInt aIndex,
          const MVPbkContactLink& aContactLink )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::ContactAddedToView(0x%x), index=%d,\
        topview(0x%x)"), &aView, aIndex, &TopView() );

    // If event comes from the base view forward the event using
    // MPbk2FilteredViewStackObserver.
    if ( &aView == iBaseViewElement->View() )
        {
        SendEventToObservers( *this, iStackObservers,
            &MPbk2FilteredViewStackObserver::ContactAddedToBaseView, aIndex,
            aContactLink );
        }

    if ( &TopView() == &aView )
        {
        // Always forward only top view events to clients
        SendEventToObservers( *this, iViewObservers,
            &MVPbkContactViewObserver::ContactAddedToView, aIndex,
            aContactLink );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactRemovedFromView
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ContactRemovedFromView
        ( MVPbkContactViewBase& aView, TInt aIndex,
          const MVPbkContactLink& aContactLink )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::ContactRemovedFromView(0x%x), index=%d,\
        topview(0x%x)"), &aView, aIndex, &TopView() );

    if ( &TopView() == &aView )
        {
        // Always forward top view events to clients
        SendEventToObservers( *this, iViewObservers,
            &MVPbkContactViewObserver::ContactRemovedFromView, aIndex,
            aContactLink );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactViewError
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::ContactViewError
        ( MVPbkContactViewBase& aView, TInt aError, TBool aErrorNotified )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::ContactViewError(0x%x), error=%d,\
        topview(0x%x)"), &aView, aError, &TopView() );

    // Check first if the aView is the base view.
    if ( iBaseViewElement->View() == &aView )
        {
        iBaseViewElement->SetViewState(
            MPbk2FilteredViewStackElement::EError );
        }
    else
        {
        // Check if the aView is one of the views in current stack
        MPbk2FilteredViewStackElement* element =
            FindElement( *iViewStack, aView );
        if ( !element )
            {
            element = FindElement( *iConstructionStack, aView );
            }

        if ( element )
            {
            element->SetViewState( MPbk2FilteredViewStackElement::EError );
            }
        }

    // Always forward view erros to clients. The view stack is not in valid
    // state and client must reset the stack
    SendEventToObservers( *this, iViewObservers,
        &MVPbkContactViewObserver::ContactViewError, aError,
        aErrorNotified );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::ContactViewObserverExtension
// --------------------------------------------------------------------------
//
TAny* CPbk2FilteredViewStack::ContactViewObserverExtension( TUid aExtensionUid )
    {
    if( aExtensionUid == KVPbkContactViewObserverExtension2Uid )
        {
        return static_cast<MVPbkContactViewObserverExtension*>( this );
        }
    return NULL;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::FilteredContactRemovedFromView
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::FilteredContactRemovedFromView(
		MVPbkContactViewBase& aView )
    {
    const TInt count = iViewObservers.Count();

    for( TInt i = 0; i < count; i++ )
       {
       MVPbkContactViewObserver* observer = iViewObservers[i];

       TAny* extension = observer->ContactViewObserverExtension(
               KVPbkContactViewObserverExtension2Uid );

       if( extension )
           {
           MVPbkContactViewObserverExtension* contactViewExtension =
                   static_cast<MVPbkContactViewObserverExtension*>( extension );

           if( contactViewExtension )
               {
               contactViewExtension->FilteredContactRemovedFromView( aView );
               }
           }
       }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::TopElement
// --------------------------------------------------------------------------
//
MPbk2FilteredViewStackElement& CPbk2FilteredViewStack::TopElement() const
    {
    if ( iViewStack && iViewStack->Count() > 0 )
        {
        return (*iViewStack)[iViewStack->Count() - 1];
        }
    else
        {
        return *iBaseViewElement;
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::TopView
// --------------------------------------------------------------------------
//
MVPbkContactViewBase& CPbk2FilteredViewStack::TopView() const
    {
    return *TopElement().View();
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::DoHandleContactViewReadyL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::DoHandleContactViewReadyL(
        MVPbkContactViewBase& aView )
    {
    // Check first if the aView is the base view.
    if ( iBaseViewElement->View() == &aView )
        {
        if ( iBaseViewElement->ViewState() ==
                MPbk2FilteredViewStackElement::EUndefined )
            {
            // The Base view is changed.
            SendBaseViewChangedEvent();
            }

        iBaseViewElement->SetViewState(
            MPbk2FilteredViewStackElement::EReady );
        }
    else
        {
        // Check if the aView is one of the views in current stack
        MPbk2FilteredViewStackElement* element =
            FindElement( *iViewStack, aView );
        if ( element )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
            ("CPbk2FilteredViewStack::DoHandleContactViewReadyL: view(0x%x)\
            in iViewStack, 1"), element->View() );

            element->SetViewState( MPbk2FilteredViewStackElement::EReady );
            if ( IsStackReadyToUpdate( *iViewStack ) )
                {
                // The views in iViewStack are ready -> try to find next
                // element from construction stack
                MPbk2FilteredViewStackElement* nextElement =
                    FindElement( *iConstructionStack, element->Level() + 1 );
                // Check if we need to create a view or not
                if ( nextElement && !nextElement->View() )
                    {
                    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                    ("CPbk2FilteredViewStack::DoHandleContactViewReadyL:\
                    create new view for text, 2") );
                    nextElement->CreateViewL(
                            *iBaseViewElement->View(),
                            *element->View(),
                            *this );
                    }
                else
                    {
                    // Not needed to create new view -> update stack.
                    UpdateStackL();
                    }
                }
            }
        // The view is not a base view and it's not in view stack. It must be
        // in the construction stack.
        else
            {
            element = FindElement( *iConstructionStack, aView );
            if ( element )
                {
                PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                ("CPbk2FilteredViewStack::DoHandleContactViewReadyL:\
                view(0x%x) in iConstructionStack, 3"), element->View() );

                element->SetViewState( MPbk2FilteredViewStackElement::EReady );
                if ( IsStackReady( *iConstructionStack ) )
                    {
                    // All construction views are ready. Stack can be updated.
                    UpdateStackL();
                    }
                else
                    {
                    // Construction stack is not ready. See if the next level
                    // view must be constructed.
                    MPbk2FilteredViewStackElement* nextElement = FindElement(
                        *iConstructionStack, element->Level() + 1 );
                    if ( nextElement && !nextElement->View() )
                        {
                        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                        ("CPbk2FilteredViewStack::DoHandleContactViewReadyL:\
                            create new view for text, 4") );
                        nextElement->CreateViewL(
                                *iBaseViewElement->View(),
                                *element->View(),
                                *this );
                        }
                    }
                }
            }
        }

    // Always forward top view events to clients if the the view stack is
    // ready.
    if ( &TopView() == &aView )
        {
        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::DoHandleContactViewReadyL:forward\
        ContactViewReady"));

        SendEventToObservers( *this, iViewObservers,
            &MVPbkContactViewObserver::ContactViewReady );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::UpdateStackL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::UpdateStackL()
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
            ("CPbk2FilteredViewStack::UpdateStackL:\
            CURRENT topview(0x%x)"), &TopView() );

    MVPbkContactViewBase* oldTopView = &TopView();

    // Create a temporary stack for elements that will be destroyed
    CElementStack* destructionStack = new ( ELeave ) CElementStack;
    CleanupStack::PushL( destructionStack );

    // Get the number of elements under destruction
    TInt count = iViewStack->Count();
    TInt destructionCount = 0;
    for ( TInt i = 0; i < count; ++i )
        {
        MPbk2FilteredViewStackElement& element = (*iViewStack)[i];
        if ( element.UnderDestruction() )
            {
            ++destructionCount;
            }
        }

    // If there are elements under destruction move them to destructionStack.
    if ( destructionCount > 0 )
        {
        // Reserve memory so that Append doesn't fail. It's important to
        // delete views in correct order.
        destructionStack->ReserveL( destructionCount );
        TInt index = iViewStack->FindFirstElementUnderDestruction();
        while ( index != KErrNotFound )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                        ("CPbk2FilteredViewStack::UpdateStackL:\
                        destroy view(0x%x) for text"),
            (*iViewStack)[index].View() );

            destructionStack->Append( &(*iViewStack)[index] );
            iViewStack->Remove( index );
            index = iViewStack->FindFirstElementUnderDestruction();
            }
        }

    // All under destruction views are now moved to destruction stack
    // Append construction views to iViewStack next if possible.
    if ( IsStackReady( *iConstructionStack ) )
        {
        iViewStack->AppendStackL( *iConstructionStack );
        }

    if ( &TopView() != oldTopView )
        {
        // The top view changed.
        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::UpdateStackL: NEW topview(0x%x)"),
        &TopView() );

        SendTopViewChangedEvent( *oldTopView );
        }
    else
        {
        // Top view updated
        SendTopViewUpdatedEvent();
        }

    CleanupStack::PopAndDestroy( destructionStack );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::DoAddObserverL
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::DoAddObserverL
        ( MVPbkContactViewObserver& aObserver, CCallback& aCallback )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
            ("CPbk2FilteredViewStack::DoAddObserverL"));

    if ( ( iViewStack->Count() == 0 &&
            iBaseViewElement->ViewState() ==
            MPbk2FilteredViewStackElement::EReady ) ||
           ( iViewStack->Count() > 0 &&  IsStackReady( *iViewStack ) ))
        {
        // The view stack is ready if
        // There is only base view and it is ready OR
        // there are also filtered views in iViewStack and they all are ready

        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                ("CPbk2FilteredViewStack::DoAddObserverL ready"));

        aObserver.ContactViewReady( *this );
        }
    // If the base view is still in EUndefined state it means that it hasn't
    // notified this instance yet.
    // If there is undefined elements in the stack it means that stack
    // is still under construction -> observer will be notified when
    // the stack becomes ready
    else if ( iBaseViewElement->ViewState() !=
              MPbk2FilteredViewStackElement::EUndefined
              && !IsStackUndefined( *iViewStack ) )
        {
        PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                ("CPbk2FilteredViewStack::DoAddObserverL unavailable"));

        aObserver.ContactViewUnavailable( *this );
        }

    DeleteCallback( aCallback );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::DoAddObserverError
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::DoAddObserverError
        ( TInt aError, CCallback& aCallback )
    {
    PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
        ("CPbk2FilteredViewStack::DoAddObserverError: aError %d"), aError );

    aCallback.Observer().ContactViewError( *this, aError, EFalse );
    DeleteCallback( aCallback );
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::SendTopViewChangedEvent
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::SendTopViewChangedEvent
        ( MVPbkContactViewBase& aOldTopView )
    {
    const TInt count = iStackObservers.Count();
    for ( TInt i = count - 1; i >= 0; --i )
        {
        TRAPD( res, iStackObservers[i]->TopViewChangedL( aOldTopView ) );
        if ( res != KErrNone )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                ("CPbk2FilteredViewStack::SendTopViewChangedEvent:error %d"),
                res );
            iStackObservers[i]->ViewStackError( res );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::SendTopViewUpdatedEvent
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::SendTopViewUpdatedEvent()
    {
    const TInt count = iStackObservers.Count();
    for ( TInt i = count - 1; i >= 0; --i )
        {
        TRAPD( res, iStackObservers[i]->TopViewUpdatedL() );
        if ( res != KErrNone )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                ("CPbk2FilteredViewStack::SendTopViewUpdatedEvent:error %d"),
                res );
            iStackObservers[i]->ViewStackError( res );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::SendBaseViewChangedEvent
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::SendBaseViewChangedEvent()
    {
    const TInt count = iStackObservers.Count();
    for ( TInt i = count - 1; i >= 0; --i )
        {
        TRAPD( res, iStackObservers[i]->BaseViewChangedL() );
        if ( res != KErrNone )
            {
            PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
                ("CPbk2FilteredViewStack::SendBaseViewChangedEvent:error %d"),
                res );
            iStackObservers[i]->ViewStackError( res );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::DeleteCallback
// --------------------------------------------------------------------------
//
void CPbk2FilteredViewStack::DeleteCallback( CCallback& aCallback )
    {
    TInt index = iCallbacks.Find( &aCallback );
    if ( index != KErrNotFound )
        {
        delete iCallbacks[index];
        iCallbacks.Remove( index );
        }
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CountLengthOfStrings
// Counts characters of array of strings
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::CountLengthOfStrings
        ( const MDesCArray& aStringArray ) const
    {
    TInt stringCount = aStringArray.MdcaCount();
    TInt totalLength = 0;
    for ( TInt i = 0; i < stringCount; ++i )
        {
        totalLength += aStringArray.MdcaPoint( i ).Length();
        }

    return totalLength;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::CopySearchWordsByLevelL
// Copies search words to new array by level
// --------------------------------------------------------------------------
//
MDesCArray* CPbk2FilteredViewStack::CopySearchWordsByLevelL
        ( const MDesCArray& aStringArray, TInt aLevel )
    {
    TInt stringNbr = 0;
    TInt stringLength = 0;
    TInt totalCharsFromBeg = 0;
    TInt charsToCopy = aLevel;
    TBool charactersLeft = ETrue;

    // Create new search level array where to copy search words
    CDesCArrayFlat* searchLevelArray = new ( ELeave ) CDesCArrayFlat(
        KSearchLevelArrayGranularity );

    while ( totalCharsFromBeg < aLevel && charactersLeft )
        {
        // Although charsToCopy may exceed the length of first string,
        // it's acceptable to use Left-function with too big value.
        searchLevelArray->AppendL(
            aStringArray.MdcaPoint( stringNbr ).Left( charsToCopy ) );
        stringLength = aStringArray.MdcaPoint( stringNbr ).Length();
        if ( charsToCopy > stringLength )
            {
            totalCharsFromBeg += stringLength;
            charsToCopy -= stringLength;
            ++stringNbr;
            }
        else
            {
            charactersLeft = EFalse;
            }
        }
    return searchLevelArray;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::IsElementsUnderDestruction
// --------------------------------------------------------------------------
//
TInt CPbk2FilteredViewStack::IsElementsUnderDestruction()
    {
    // Get the number of elements under destruction
    const TInt count = iViewStack->Count();
    TBool result( EFalse );
    for ( TInt i = 0; i < count; ++i )
        {
        MPbk2FilteredViewStackElement& element = (*iViewStack)[i];
        if ( element.UnderDestruction() )
            {
            result = ETrue;
            break;
            }
        }
    return result;
    }

// --------------------------------------------------------------------------
// CPbk2FilteredViewStack::__DbgTestInvariant
// Debug time checking. All stack elements must be in order
// --------------------------------------------------------------------------
//
#if defined(_DEBUG)
void CPbk2FilteredViewStack::__DbgTestInvariant() const
 {
    TInt filterLevel = 0;
    TInt count = iConstructionStack->Count();
    for ( TInt i = 0; i < count; ++i )
        {
        __ASSERT_DEBUG( (*iConstructionStack)[i].Level() > filterLevel,
            Panic( EInvariantConstructionStackNotIntOrder ) );
        filterLevel = (*iConstructionStack)[i].Level();
        }

    filterLevel = 0;
    count = iViewStack->Count();
    for ( TInt i = 0; i < count; ++i )
        {
        __ASSERT_DEBUG( (*iViewStack)[i].Level() > filterLevel,
            Panic( EInvariantViewStackNotIntOrder ) );
        filterLevel = (*iViewStack)[i].Level();
        }
 }

 #endif // _DEBUG

// End of File
