/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Implementation of mpxaddtracksdialogimp
*
*/


// INCLUDE FILES
#include <eikclb.h>
#include <eikclbd.h>
#include <eikimage.h>
#include <eiktxlbx.h>
#include <aknappui.h>
#include <akntitle.h>
#include <akncontext.h>
#include <aknlists.h>
#include <aknsfld.h>
#include <gulicon.h>
#include <AknIconArray.h>
#include <AknIconUtils.h>
#include <StringLoader.h>
#include <aknnotewrappers.h>
#include <e32base.h>
#include <barsread.h>
#include <bautils.h>
#include <data_caging_path_literals.hrh>
#include <textresolver.h>

#include <mpxlog.h>
#include <mpxcollectionutility.h>
#include <mpxcollectionplaylist.h>
#include <mpxaddtracksdialog.rsg>
#include <mpxaddtracksdialog.mbg>

#include <mpxcollectionhelperfactory.h>
#include <mpxcollectionplaylist.h>
#include <mpxmediageneraldefs.h>
#include <mpxmediacontainerdefs.h>
#include <mpxmedia.h>
#include <mpxmediamusicdefs.h>
#include <mpxmediaarray.h>
#include <mpxmessagegeneraldefs.h>
#include <mpxcollectionmessage.h>
#include <mpxuser.h>

#include "mpxcommonuihelper.h"
#include "mpxaddtracksdialogcommon.h"
#include "mpxaddtracksdialog.hrh"
#include "mpxaddtracksdialog.h"
#include "mpxaddtrackslistmodel.h"
#include "mpxaddtrackslbxarray.h"
#include <mpxlbxextendedfeatures.h>

// CONSTANTS
const TInt KMPXATMaxExpandedGroupNum = 1;

// Animation timer defined in UI spec
const TInt KAnimationFrame1Timer = 50000;
const TInt KAnimationFrame2Timer = 100000;
const TInt KAnimationFrame3Timer = 100000;
const TInt KAnimationFrame4Timer = 500000;
const TInt KAnimationFrameTimerInterval = 1;

const TInt KMaxTitleLength = 261;  // 255 + 6 for tab

_LIT( KMPXAddTracksDialogRscPath, "mpxaddtracksdialog.rsc" );
_LIT(KMPXSpace," ");

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

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXAddTracksDialog* CMPXAddTracksDialog::NewL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::NewL" );
    CMPXAddTracksDialog* self = CMPXAddTracksDialog::NewLC();
    CleanupStack::Pop();
    return self;
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXAddTracksDialog* CMPXAddTracksDialog::NewLC()
    {
    MPX_FUNC( "CMPXAddTracksDialog::NewLC" );
    CMPXAddTracksDialog* self = new ( ELeave ) CMPXAddTracksDialog();
    CleanupStack::PushL( self );
    self->LoadResourceL();
    self->ConstructL( R_AVKON_SOFTKEYS_EMPTY );
    return self;
    }

// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
//
EXPORT_C CMPXAddTracksDialog::~CMPXAddTracksDialog()
    {
    MPX_FUNC( "CMPXAddTracksDialog::~CMPXAddTracksDialog" );

    TRAP_IGNORE( RestorePreviousStatusPaneL() );

    if ( iCollectionUtility )
        {
        iCollectionUtility->Close();
        }
    if ( iCollectionUiHelper )
        {
        iCollectionUiHelper->Cancel();
        iCollectionUiHelper->Close();
        }
    if ( iOrigIcon )
        {
        delete iOrigIcon;
        }
    if ( iNewIcon )
        {
        delete iNewIcon;
        }
    if ( iLastFindTxt )
        {
        delete iLastFindTxt;
        }
    iMatchedSongArray.Reset();
    iMatchedGroupArray.Reset();

    delete iOrigTitle;
    delete iListModel;
    delete iNoSongText;
    delete iNoMatchText;

    if( iPeriodic )
        {
        delete iPeriodic;
        }
    delete iLbxExtFeat;

    if ( iResourceOffset )
        {
        iEikonEnv->DeleteResourceFile( iResourceOffset );
        }

    if ( iCategoryArray )
        {
        iCategoryArray->Reset();
        delete iCategoryArray;
        }

    if ( iAllSongsArray )
        {
        iAllSongsArray->Reset();
        delete iAllSongsArray;
        }

    delete iCommonUiHelper;
    delete iIdle;
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::CMPXAddTracksDialog
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMPXAddTracksDialog::CMPXAddTracksDialog():
    CAknSelectionListDialog( iLastIndex, NULL, 0 ),
    iAnimIconIndex( 0 ),
    iSongAllGroupExist( ETrue )
    {
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::ConstructL
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::ConstructL( TInt aResource )
    {
    MPX_FUNC( "CMPXAddTracksDialog::ConstructL" );
    CAknSelectionListDialog::ConstructL( aResource );
    iCollectionUtility = MMPXCollectionUtility::NewL( this, KMcModeDefault );
    iCollectionUiHelper = CMPXCollectionHelperFactory::NewCollectionUiHelperL();
    iCommonUiHelper = CMPXCommonUiHelper::NewL();
    iNoSongText = StringLoader::LoadL( R_MPX_CUI_CATEGORY_ADDSONGVIEW_LBX_NOSONGTEXT );
    iNoMatchText = StringLoader::LoadL( R_MPX_CUI_CATEGORY_ADDSONGVIEW_LBX_EMPTYTEXT );
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::BackupPreviousStatusPaneL()
// Backup previous status pane to internal members.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::BackupPreviousStatusPaneL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::BackupPreviousStatusPaneL" );
    CEikStatusPane* sp = iAvkonAppUi->StatusPane();

    // Backup title pane
    iTitlePane = static_cast<CAknTitlePane*>
        ( sp->ControlL( TUid::Uid( EEikStatusPaneUidTitle ) ) );
    iOrigTitle = iTitlePane->Text()->AllocL();

    // Backup context pane only if new context icon is created
    if ( iNewIcon )
        {
        iContextPane = static_cast<CAknContextPane*>
            ( sp->ControlL( TUid::Uid( EEikStatusPaneUidContext ) ) );
        iOrigIcon = iContextPane->SwapPicture( iNewIcon );
        iNewIcon = NULL;
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::RestorePreviousStatusPaneL()
// Restore previous status pane from internal members.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::RestorePreviousStatusPaneL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::RestorePreviousStatusPaneL" );
    if ( iOrigTitle )
        {
        // Set original title pane
        iTitlePane->SetTextL( *iOrigTitle );
        }

    // Restore original context icon
    if ( iOrigIcon )
        {
        iNewIcon = iContextPane->SwapPicture( iOrigIcon );
        iOrigIcon = NULL;
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::CreateContextIconL()
// Create context icon for add tracks dialog.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::CreateContextIconL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::CreateContextIconL" );
    TParse parse;
    parse.Set( KMPXAddTracksBmpFile, &KDC_APP_RESOURCE_DIR, NULL );
    TFileName iconFile( parse.FullName() );
    User::LeaveIfError( MPXUser::CompleteWithDllPath( iconFile ) );

    MAknsSkinInstance* skin( AknsUtils::SkinInstance() );
    CFbsBitmap* bitmap = NULL;
    CFbsBitmap* mask = NULL;

    AknsUtils::CreateIconLC(
            skin,
            KAknsIIDNone,
            bitmap,
            mask,
            iconFile,
            EMbmMpxaddtracksdialogQgn_graf_mup_ctx_addsong,
            EMbmMpxaddtracksdialogQgn_graf_mup_ctx_addsong_mask );

    iNewIcon = new ( ELeave ) CEikImage();
    iNewIcon->SetPicture( bitmap, mask );
    iNewIcon->SetPictureOwnedExternally( EFalse );

    CleanupStack::Pop( 2 ); // bitmap, mask
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::UpdateSoftkeyL()
// Update softkey.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::UpdateSoftkeyL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::UpdateSoftkeyL" );
    TInt resId( 0 );
    TInt index = CurrentLbxItemIndex();
    CAknSearchField* findBox = FindBox();
    TBool findBoxVisible = ( findBox->IsVisible() ) && ( findBox->IsFocused() );

    MPX_DEBUG2( "CMPXAddTracksDialog::UpdateSoftkeyL index = %d", index);

    iListBox->View()->SetListEmptyTextL(
        findBoxVisible? *iNoMatchText : *iNoSongText );
    if ( iListBox->Model()->NumberOfItems() == 0 )
        {
        // redraw empty text
        iListBox->DrawDeferred();
        }

    if ( index == KErrNotFound )
        {
        resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_EMPTY_DONE;
        if ( findBoxVisible )
            {
            resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_EMPTY_CANCEL;
            }
        }
    else
        {
        TInt id = iListModel->TrackTypeL( index );
        if ( id == EMPXGroup )
            {
            resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_EXPAND_DONE;
            if ( findBoxVisible )
                {
                resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_EXPAND_CANCEL;
                }
            if ( iListModel->IsExpandedIndex( index ) )
                {
                resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_COLLAPSE_DONE;
                if ( findBoxVisible )
                    {
                    resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_COLLAPSE_CANCEL;
                    }
                }
            }
        else
            {
            resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_ADD_DONE;
            if ( findBoxVisible )
                {
                resId = R_MPX_CUI_ADDTRACKS_SOFTKEYS_ADD_CANCEL;
                }
            }
        }
    ButtonGroupContainer().SetCommandSetL( resId );
    
    // A temporary RSK command observer is registered so that FindBox 
    // cannot register its own RSK command observer. This way it will
    // not handle the RSK but the ProcessCommandL method in this dialog
    // will do it. The observer must not be removed, because if there 
    // is no observer registered when the find box is closed, then 
    // the execution of CAknSearchField::RestorePopupCBA will raise a
    // panic.
   	if( !ButtonGroupContainer().UpdatedCommandObserverExists( 
		CEikButtonGroupContainer::ERightSoftkeyPosition ) )
   		{
   		MPX_DEBUG1( "CMPXAddTracksDialog::UpdateSoftkeyL: Adding RSK observer" );
   		ButtonGroupContainer().UpdateCommandObserverL( 2, *this );
   		}
    ButtonGroupContainer().DrawDeferred();
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::UpdateListboxByCategoryL()
// Update listbox content by category.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::UpdateListboxByCategoryL( const CMPXMedia& aMedia  )
    {
    MPX_FUNC( "CMPXAddTracksDialog::UpdateListboxByCategoryL" );
    // Reset tracks list
    iListModel->ResetTracksList();

    // Insert "Songs (all)" to model
    CMPXMediaArray* media =
        const_cast<CMPXMediaArray*>(aMedia.Value<CMPXMediaArray>(
        KMPXMediaArrayContents ) );
    User::LeaveIfNull( media );

    delete iCategoryArray;
    iCategoryArray = NULL;
    iCategoryArray = CMPXMediaArray::NewL();
    TInt count = media->Count();
    for ( TInt i = 0; i < count; i++ )
        {
        CMPXMedia* orig( media->AtL( i ) );
        User::LeaveIfNull( orig );
        CMPXMedia* copy( CMPXMedia::NewL( *orig ) );
        CleanupStack::PushL( copy );
        iCategoryArray->AppendL( copy );
        CleanupStack::Pop( copy );
        }
    if ( count > 0 )
        {
        // change display text to unknown if the last entry is null
        CMPXMedia* aryMedia( media->AtL( count - 1 ) );
        User::LeaveIfNull( aryMedia );
        if ( aryMedia->ValueText( KMPXMediaGeneralTitle ).Length() == 0 )
            {
            HBufC* text = StringLoader::LoadLC(
                R_MPX_QTN_MP_UNKNOWN );
            CMPXMedia* catMedia( iCategoryArray->AtL( count - 1 ) );
            User::LeaveIfNull( catMedia );
            catMedia->SetTextValueL( KMPXMediaGeneralTitle, *text );
            aryMedia->SetTextValueL( KMPXMediaGeneralTitle, *text );
            CleanupStack::PopAndDestroy( text );
            }
        CMPXMedia* entry = CMPXMedia::NewL();
        CleanupStack::PushL( entry );
        HBufC* string =
            StringLoader::LoadLC( R_MPX_CUI_ADDSONGS_ADD_ALLSONGS_TXT );
        entry->SetTextValueL(
            KMPXMediaGeneralTitle, *string );
        media->Insert( entry, 0 );
        CleanupStack::PopAndDestroy( string );
        CleanupStack::Pop( entry );
        // Insert artists to model
        iListModel->InsertGroupsL( *media, -1, EMPXSong );
        HandleItemArrayChangeL();
        iSongAllGroupExist = ETrue;
        }
    else
        {
        MPX_DEBUG1( "CMPXAddTracksDialog::UpdateListboxByCategoryL no song" );
        // no song
        iSongAllGroupExist = EFalse;
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::UpdateListboxByCategoryforFindL()
// Update listbox content by category after find.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::UpdateListboxByCategoryforFindL(
    TBool aFind, TInt aSongNum)
    {
    MPX_FUNC( "CMPXAddTracksDialog::UpdateListboxByCategoryforFindL" );
    if( aFind )
        {
        if( aSongNum != 0 )
            {
            // Insert "Songs (%N)" to list box model, change "%N" to matched songs' number
            CMPXMediaArray* media = CMPXMediaArray::NewL();
            CleanupStack::PushL( media );
            CMPXMedia* entry = CMPXMedia::NewL();
            CleanupStack::PushL( entry );
            HBufC* string = string = StringLoader::LoadLC(
                            R_MPX_CUI_ADDSONGS_ADD_SONGS_NUM_TXT, aSongNum );
            entry->SetTextValueL( KMPXMediaGeneralTitle, *string );
            media->AppendL( entry );
            iListModel->InsertGroupsL( *media, 0, EMPXSong );
            CleanupStack::PopAndDestroy( string );
            CleanupStack::Pop( entry );
            CleanupStack::PopAndDestroy( media );
            iSongAllGroupExist = ETrue;
            }
        else
            {
            iSongAllGroupExist = EFalse;
            }

        // Insert found groups
        TInt matchedGroupCount = iMatchedGroupArray.Count();
        if( matchedGroupCount > 0 )
            {
            CMPXMediaArray* media = CMPXMediaArray::NewL();
            CleanupStack::PushL( media );
            for ( TInt i = 0; i < matchedGroupCount; i++ )
                {
                TInt catIndex = iMatchedGroupArray[ i ];
                CMPXMedia* catMedia( iCategoryArray->AtL( catIndex ) );
                User::LeaveIfNull( catMedia );
                CMPXMedia* entry = CMPXMedia::NewL( *catMedia  );
                CleanupStack::PushL( entry );
                media->AppendL( entry );
                CleanupStack::Pop( entry );
                }
            if( !iSongAllGroupExist )
                {
                iListModel->InsertGroupsL( *media, 0 );
                }
            else
                {
                iListModel->InsertGroupsL( *media, 1 );
                }
            CleanupStack::PopAndDestroy( media );
            }
        }
    else
        {
        iListModel->ResetTracksList();
        }

    HandleItemArrayChangeL();
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::CurrentLbxItemIndex
// Gets current listbox item index.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CMPXAddTracksDialog::CurrentLbxItemIndex() const
    {
    MPX_FUNC( "CMPXAddTracksDialog::CurrentLbxItemIndex" );
    CTextListBoxModel* lbxModel = iListBox->Model();
    CAknSearchField* findBox = FindBox();
    TInt currentItem = iListBox->CurrentItemIndex();
    TInt count = lbxModel->NumberOfItems();

    // Make sure there's at least one item in the listbox
    if ( findBox && count )
        {
        CAknFilteredTextListBoxModel* filteredLbxModel =
            static_cast<CAknFilteredTextListBoxModel*>( lbxModel );
        CAknListBoxFilterItems* filterItems = filteredLbxModel->Filter();

        // Get the real listbox index
        currentItem = filterItems->FilteredItemIndex( currentItem );
        }
    return currentItem;
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::CurrentListItemCount
// Return number of listbox items shown in the current listbox.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CMPXAddTracksDialog::CurrentListItemCount() const
    {
    MPX_FUNC( "CMPXAddTracksDialog::CurrentListItemCount" );
    CTextListBoxModel* lbxModel = iListBox->Model();
    TInt count = lbxModel->NumberOfItems();
    CAknSearchField* findBox = FindBox();

    if ( findBox && count )
        {
        CAknFilteredTextListBoxModel* filteredLbxModel =
            static_cast<CAknFilteredTextListBoxModel*>( lbxModel );
        CAknListBoxFilterItems* filterItems = filteredLbxModel->Filter();

        count = filterItems->FilteredNumberOfItems();
        }
    return count;
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::HandleItemArrayChangeL
// When you change the list item array you should call this method.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleItemArrayChangeL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::HandleItemArrayChangeL" );
    TInt currentIdx = iListBox->CurrentItemIndex();
    TInt topIdx = iListBox->TopItemIndex();
    CListBoxView* lbxView = iListBox->View();
    TInt offset =
        lbxView->NumberOfItemsThatFitInRect( lbxView->ViewRect() ) - 1;

    CAknFilteredTextListBoxModel* lbxModel =
        static_cast<CAknFilteredTextListBoxModel*>( iListBox->Model() );
    CAknSearchField* findBox = FindBox();
    lbxModel->Filter()->SetSearchField( NULL );//for find the items
    // Update item array
    iLbxFilterItems->HandleItemArrayChangeL();
    lbxModel->Filter()->SetSearchField( findBox );// for laylout of list

    TInt itemCount = CurrentListItemCount();
    if ( itemCount )
        {
        TInt lastItemIdx = itemCount - 1;
        if ( topIdx + offset > lastItemIdx )
            {
            topIdx = lastItemIdx - offset;
            }
        if ( topIdx < 0 )
            {
            topIdx = 0;
            }
        if ( currentIdx < 0 )
            {
            currentIdx = 0;
            }
        if ( currentIdx > lastItemIdx )
            {
            currentIdx = lastItemIdx;
            }

        iListBox->SetTopItemIndex( topIdx );
        iListBox->SetCurrentItemIndexAndDraw( currentIdx );
        iListBox->DrawDeferred();
        }
    UpdateSoftkeyL();
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::HighlightListItem
// Highlight list item.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HighlightListItem( TInt aIndex )
    {
    MPX_FUNC( "CMPXAddTracksDialog::HighlightListItem" );
    const TInt KMPItemOffset = 1;
    TInt itemCount = CurrentListItemCount();

    if ( itemCount )
        {
        TInt bottomIdx = iListBox->BottomItemIndex();
        TInt topIdx = iListBox->TopItemIndex();

        // Rearrange the index if aIndex is the bottom item
        if ( aIndex == bottomIdx )
            {
            TInt lastItemIdx = itemCount - 1;
            if ( aIndex != lastItemIdx )
                {
                topIdx += KMPItemOffset;
                if ( topIdx > lastItemIdx )
                    {
                    topIdx = lastItemIdx;
                    }
                }
            }

        if ( topIdx < 0 )
            {
            topIdx = 0;
            }

        iListBox->SetTopItemIndex( topIdx );
        iListBox->SetCurrentItemIndexAndDraw( aIndex );
        iListBox->DrawDeferred();
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::ExpandCurrentGroupL()
// Expand current group.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::ExpandCurrentGroupL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::ExpandCurrentGroupL" );
    TInt index = -1;
    if ( iExpandAllSongs )
        {
        index = EMPXAddTracksAddAllSongs;
        }
    else
        {
        index = CurrentLbxItemIndex();
        }
    TInt oldIndex = index;

    // Make sure current item is expandable
    if ( ( index != KErrNotFound ) &&
        ( iListModel->TrackTypeL( index ) == EMPXGroup ) &&
        ( !iListModel->IsExpandedIndex( index ) ) )
        {
        // Make sure the expanded group count is
        // under KMPXATMaxExpandedGroupNum.
        while ( iListModel->ExpandedGroupsCount() >=
                KMPXATMaxExpandedGroupNum )
            {
            // Remove all tracks from the oldest expanded group
            oldIndex = iListModel->OldestExpandedGroupIndex();
            TInt count = iListModel->RemoveAllTracksFromGroupL( oldIndex );
            // Update the index accordingly
            if ( index > oldIndex )
                {
                index -= count;
                }
            }

        if ( index == EMPXAddTracksAddAllSongs && iSongAllGroupExist )
            {
            if ( iAllSongsArray )
                {
                // Insert tracks for all songs
                iExpandAllSongs = EFalse;
                TInt matchedSongCount = iMatchedSongArray.Count();
                if ( matchedSongCount == 0 )
                    {
                    iListModel->InsertTracksUnderGroupL( *iAllSongsArray, index );
                    TInt diff = oldIndex - index;
                    TInt newCurrentIdx = iListBox->CurrentItemIndex() - diff;
                    if ( newCurrentIdx < 0 )
                        {
                        newCurrentIdx = 0;
                        }
                    TInt newTopIdx = iListBox->TopItemIndex() - diff;
                    if ( newTopIdx < 0 )
                        {
                        newTopIdx = 0;
                        }
                    iListBox->SetTopItemIndex( newTopIdx );
                    iListBox->SetCurrentItemIndex( newCurrentIdx );
                    }
                else
                    {
                    TInt matchedSongCount = iMatchedSongArray.Count();
                    for ( TInt i = 0; i < matchedSongCount; i++ )
                        {
                        TInt songIndex = iMatchedSongArray[ i ];
                        CMPXMedia* origMedia( iAllSongsArray->AtL( songIndex ) );
                        User::LeaveIfNull( origMedia );
                        iListModel->InsertTrackL( *origMedia, index + 1 + i );
                        }
                    iListModel->MarkExpandedL( index, matchedSongCount );
                    }
                HandleItemArrayChangeL();
                HighlightListItem( index );
                UpdateSoftkeyL();
                }
            else
                {
                MPX_DEBUG1( "CMPXAddTracksDialog::ExpandCurrentGroupL all songs NOT READY" );
                iExpandAllSongs = ETrue;
                HBufC* string = StringLoader::LoadLC(
                    R_MPX_QTN_NMP_NOTE_OPENING );
                iCommonUiHelper->ShowWaitNoteL( *string,
                    R_AVKON_SOFTKEYS_EMPTY, EFalse, NULL );
                CleanupStack::PopAndDestroy( string );
                }
            }
        else
            {
            if ( iCurrentOp == EMPXOpIdle )
                {
                HandleItemArrayChangeL();
                HBufC* string = StringLoader::LoadLC(
                    R_MPX_QTN_NMP_NOTE_OPENING );
                iCommonUiHelper->ShowWaitNoteL( *string,
                    R_AVKON_SOFTKEYS_EMPTY, EFalse, NULL );
                CleanupStack::PopAndDestroy( string );
                CMPXMedia* media = CMPXMedia::NewL();
                CleanupStack::PushL( media );
                media->SetTObjectValueL<TMPXGeneralType>(
                    KMPXMediaGeneralType, EMPXGroup );
                media->SetTObjectValueL<TMPXGeneralCategory>(
                    KMPXMediaGeneralCategory, EMPXSong );
                media->SetTObjectValueL<TMPXItemId>(
                    KMPXMediaGeneralId, iListModel->TrackIdL( index ) );
                RArray<TMPXAttribute> attrs;
                CleanupClosePushL( attrs );
                attrs.Append(
                    TMPXAttribute( KMPXMediaIdGeneral,
                        EMPXMediaGeneralTitle | EMPXMediaGeneralId
                        | EMPXMediaGeneralType |  EMPXMediaGeneralCategory |
                        EMPXMediaGeneralFlags ) );
                iCurrentOp = EMPXOpGetSongs;
                iCurrentGroupIndex = index;
                iCollectionUtility->Collection().FindAllL( *media, attrs.Array(), *this );
                CleanupStack::PopAndDestroy( &attrs );
                CleanupStack::PopAndDestroy( media );
                }
            else
                {
                MPX_DEBUG1( "CMPXAddTracksDialog::ExpandCurrentGroupL find all busy, ignoring" );
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::GotoClosestGroupL()
// Goto the closest group.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::GotoClosestGroupL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::GotoClosestGroupL" );
    // Get the closest expanded index
    TInt origIndex = CurrentLbxItemIndex();
    if ( origIndex != KErrNotFound )
        {
        TInt index = iListModel->ClosestExpandedGroupIdx( origIndex );
        if ( index != KErrNotFound )
            {
            // Highlight on the group
            HighlightListItem( index );

            UpdateSoftkeyL();
            }
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::CollapseCurrentGroupL()
// Collapse current group.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::CollapseCurrentGroupL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::CollapseCurrentGroupL" );
    TInt index = CurrentLbxItemIndex();

    if ( ( index != KErrNotFound ) &&
        ( iListModel->IsExpandedIndex( index ) ) )
        {
        // Remove all tracks from this group
        iListModel->RemoveAllTracksFromGroupL( index );

        // Update listbox and softkey
        HandleItemArrayChangeL();
        HighlightListItem( index );
        UpdateSoftkeyL();
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::AddCurrentTrackToPlaylistL()
// Add current track to playlist.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::AddCurrentTrackToPlaylistL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::AddCurrentTrackToPlaylistL" );
    TInt index = CurrentLbxItemIndex();
    iSelectIndex = index;

    if ( index != KErrNotFound && iPlaylistId != 0 )
        {
        CMPXMedia* media = CMPXMedia::NewL();
        CleanupStack::PushL( media );
        media->SetTObjectValueL<TMPXItemId>(
            KMPXMediaGeneralId, iPlaylistId );
        media->SetTObjectValueL<TMPXGeneralType>(
            KMPXMediaGeneralType, EMPXItem );
        media->SetTObjectValueL<TMPXGeneralCategory>(
            KMPXMediaGeneralCategory, EMPXPlaylist );

        CMPXMediaArray* mediaArray = CMPXMediaArray::NewL();
        CleanupStack::PushL( mediaArray );
        CMPXMedia* song = CMPXMedia::NewL( iListModel->MediaL( index ) );
        CleanupStack::PushL( song );
        mediaArray->AppendL( song );
        CleanupStack::Pop( song );
        media->SetCObjectValueL( KMPXMediaArrayContents, mediaArray );
        media->SetTObjectValueL( KMPXMediaArrayCount, 1 );
        iCollectionUiHelper->AddL( *media, this );
        CleanupStack::PopAndDestroy( 2, media );

        // Enable animation
        // Animation icon index is 3 -> 5
        iAnimIconIndex = CMPXAddTracksLbxArray::EMPXATLbxIconSongAddedAnim1;
        iListModel->SetAnimationIconNum( iSelectIndex, iAnimIconIndex );
        HighlightListItem( iSelectIndex );

        // first icon animation
        if ( iPeriodic->IsActive() )
            {
            iPeriodic->Cancel();
            }
        iPeriodic->Start( TTimeIntervalMicroSeconds32(KAnimationFrame1Timer),
                            TTimeIntervalMicroSeconds32(KAnimationFrameTimerInterval),
                            TCallBack( AnimExpireL, this ) );
        }
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::DoFindL
// Execute find for group and songs.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::DoFindL( void )
    {
    MPX_FUNC( "CMPXAddTracksDialog::DoFindL" );

    if ( !iAllSongsArray )
        {
        iDoFind = ETrue;
        HBufC* string = StringLoader::LoadLC(
            R_MPX_QTN_NMP_NOTE_OPENING );
        iCommonUiHelper->ShowWaitNoteL( *string,
            R_AVKON_SOFTKEYS_EMPTY, EFalse, NULL );
        CleanupStack::PopAndDestroy( string );
        return;
        }

    // reset all group to default state
    iListModel->ResetTracksList();

    iMatchedSongArray.Reset();
    iMatchedGroupArray.Reset();

    // start find
    TInt searchTextNum = iLastFindTxt->Length();
    TInt songAllMatchedNum = 0;

    TBufC<KMaxTitleLength> bufSearchText;
    TPtr searchText = bufSearchText.Des();
    TBufC<KMaxTitleLength> bufSearchTextwithSpace;
    TPtr searchTextwithSpace = bufSearchTextwithSpace.Des();
    TBool find = EFalse;


    // Reset find result
    iListModel->SetFindSongsFlag( EFalse );
    // current searching is to compare the text string with list box text
    // if they are match, means found;otherwise, not found
    // searching way can be changed if needed
    if( searchTextNum != 0 )
        {
        TInt listCount = iCategoryArray->Count();
        for( TInt i = 0; i < listCount; i ++ )
            {
            CMPXMedia* media( iCategoryArray->AtL( i ) );
            User::LeaveIfNull( media );
            const TDesC& constTitle = media->ValueText(
                KMPXMediaGeneralTitle );
            TBufC<KMaxTitleLength> titleBuf( constTitle );
            TPtr title = titleBuf.Des();
            searchText = iLastFindTxt->Left( searchTextNum );

            title.LowerCase();
            searchText.LowerCase();

            if( title.Find( KMPXSpace ) != KErrNotFound )
                {
                searchTextwithSpace = searchText;
                // insert space if there is not, to make every search same
                searchTextwithSpace.Insert( 0, KMPXSpace );
                }

            // find way: match first several characters, if there is space in the
            // middle of the track name, match the string also after the space
            if ( ( ( searchTextwithSpace.Length() > 0 ) &&
                ( title.Find( searchTextwithSpace ) != KErrNotFound ) ) ||
                ( title.Left( searchTextNum ) == searchText ) )
                {
                iMatchedGroupArray.Append( i );
                find = ETrue;
                }
            }

        TInt songCount = iAllSongsArray->Count();
        for ( TInt i = 0; i < songCount; i++ )
            {
            CMPXMedia* media( iAllSongsArray->AtL( i ) );
            User::LeaveIfNull( media );
            const TDesC& constTitle = media->ValueText(
                KMPXMediaGeneralTitle );
            TBufC<KMaxTitleLength> titleBuf( constTitle );
            TPtr title = titleBuf.Des();
            searchText = iLastFindTxt->Left( searchTextNum );

            title.LowerCase();
            searchText.LowerCase();

            if( title.Find( KMPXSpace ) != KErrNotFound )
                {
                searchTextwithSpace = searchText;
                // insert space if there is not, to make every search same
                searchTextwithSpace.Insert( 0, KMPXSpace );
                }

            // find way: match first several characters, if there is space in the
            // middle of the track name, match the string also after the space
            if ( ( ( searchTextwithSpace.Length() > 0 ) &&
                ( title.Find( searchTextwithSpace ) != KErrNotFound ) ) ||
                ( title.Left( searchTextNum ) == searchText ) )
                {
                // matched songs
                songAllMatchedNum ++;
                iMatchedSongArray.Append( i );
                find = ETrue;
                iListModel->SetFindSongsFlag( ETrue );
                }
            }
        // update according to searching result
        UpdateListboxByCategoryforFindL( find, songAllMatchedNum );
        }
    else if ( iAllSongsArray->Count() > 0 )
        {
        // Songs(All) node is shown
        iListModel->SetFindSongsFlag( ETrue );
        CMPXMediaArray* media = CMPXMediaArray::NewL();
        CleanupStack::PushL( media );
        CMPXMedia* entry = CMPXMedia::NewL();
        CleanupStack::PushL( entry );
        HBufC* string =
            StringLoader::LoadLC( R_MPX_CUI_ADDSONGS_ADD_ALLSONGS_TXT );
        entry->SetTextValueL( KMPXMediaGeneralTitle, *string );
        media->Insert( entry, 0 );
        CleanupStack::PopAndDestroy( string );
        CleanupStack::Pop( entry );
        iListModel->InsertGroupsL( *media, 0, EMPXSong );
        iListModel->InsertGroupsL( *iCategoryArray, 1 );
        CleanupStack::PopAndDestroy( media );
        iSongAllGroupExist = ETrue;
        HandleItemArrayChangeL();
        }

    // Always highlight the first item
    HighlightListItem( 0 );
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::DoAnimL
// Execute animation.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::DoAnimL(void)
    {
    MPX_FUNC( "CMPXAddTracksDialog::DoAnimL" );
    iPeriodic->Cancel();

    // remove animation icon
    if( iAnimIconIndex ==
            CMPXAddTracksLbxArray::EMPXATLbxIconSongAddedAnim4 )
        {
        return;
        }
    iAnimIconIndex ++;
    iListModel->SetAnimationIconNum( iSelectIndex, iAnimIconIndex );
    HighlightListItem(iSelectIndex);

    TTimeIntervalMicroSeconds32 interval = 0;
    if( iAnimIconIndex ==
                CMPXAddTracksLbxArray::EMPXATLbxIconSongAddedAnim2 )
        {
        interval = KAnimationFrame2Timer;
        }
    else if(iAnimIconIndex ==
                CMPXAddTracksLbxArray::EMPXATLbxIconSongAddedAnim3 )
        {
        interval = KAnimationFrame3Timer;
        }
    else if(iAnimIconIndex ==
                CMPXAddTracksLbxArray::EMPXATLbxIconSongAddedAnim4 )
        {
        interval = KAnimationFrame4Timer;
        }
    iPeriodic->Start( interval,
                    TTimeIntervalMicroSeconds32(KAnimationFrameTimerInterval ),
                    TCallBack( AnimExpireL, this ) );

    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::AnimExpireL
// Animation call back method.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CMPXAddTracksDialog::AnimExpireL( TAny* aObject )
    {
    MPX_FUNC( "CMPXAddTracksDialog::AnimExpireL" );
    ( ( CMPXAddTracksDialog* )( aObject ) )->DoAnimL();
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::StopAnimL
// Stop animation.
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::StopAnimL( TBool aHighlightCurrent )
    {
    MPX_FUNC( "CMPXAddTracksDialog::StopAnimL" );
    if( ( iPeriodic->IsActive() ||
        iAnimIconIndex ==
            CMPXAddTracksLbxArray::EMPXATLbxIconSongAddedAnim4 ) )
        {
        iAnimIconIndex = 0;
        iPeriodic->Cancel();
        iListModel->SetAnimationIconNum( iSelectIndex, 0 );
        
        // highlights the item pointed by iSelectIndex. 
        // if Efalse, then iSelectIndex does not match to the tapped item but it is updated later when AddCurrentTractToPlaylistL is called.
        if ( aHighlightCurrent )
            {
            TInt bottomIdx = iListBox->BottomItemIndex();
            TInt topIdx = iListBox->TopItemIndex();
            if ( topIdx <= iSelectIndex && iSelectIndex <= bottomIdx )
                {
                HighlightListItem( iSelectIndex );
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// load resources
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::LoadResourceL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::LoadResourceL" );
    CCoeEnv* coeEnv = iEikonEnv;
    TParse parse;
    parse.Set( KMPXAddTracksDialogRscPath, &KDC_APP_RESOURCE_DIR, NULL );
    TFileName resourceFile( parse.FullName() );
    User::LeaveIfError( MPXUser::CompleteWithDllPath( resourceFile ) );
    BaflUtils::NearestLanguageFile( coeEnv->FsSession(), resourceFile );
    iResourceOffset = coeEnv->AddResourceFileL( resourceFile );
    }

// ---------------------------------------------------------------------------
// start find categories
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::LoadCategoriesL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::LoadCategoriesL" );
    CMPXMedia* media = CMPXMedia::NewL();
    CleanupStack::PushL( media );
    media->SetTObjectValueL<TMPXGeneralType>(
        KMPXMediaGeneralType, EMPXGroup );
    media->SetTObjectValueL<TMPXGeneralCategory>(
        KMPXMediaGeneralCategory, EMPXArtist );
    RArray<TMPXAttribute> attrs;
    CleanupClosePushL( attrs );
    attrs.Append(
        TMPXAttribute( KMPXMediaIdGeneral,
            EMPXMediaGeneralTitle | EMPXMediaGeneralId
            | EMPXMediaGeneralType |  EMPXMediaGeneralCategory |
            EMPXMediaGeneralFlags ) );
    iCurrentOp = EMPXOpGetCategory;
    iCollectionUtility->Collection().FindAllL( *media, attrs.Array(), *this );
    CleanupStack::PopAndDestroy( &attrs );
    CleanupStack::PopAndDestroy( media );
    }

// -----------------------------------------------------------------------------
// Handle collection message
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::DoHandleCollectionMessageL( const CMPXMessage& aMessage )
    {
    MPX_FUNC( "CMPXAddTracksDialog::DoHandleCollectionMessageL" );
    TMPXMessageId id( aMessage.ValueTObjectL<TMPXMessageId>( KMPXMessageGeneralId ) );
    if ( KMPXMessageGeneral == id )
        {
        if( aMessage.ValueTObjectL<TInt>( KMPXMessageGeneralEvent ) ==
            TMPXCollectionMessage::EBroadcastEvent )
            {
            TInt op( aMessage.ValueTObjectL<TInt>( KMPXMessageGeneralType ) );
            if ( op == EMcMsgUSBMTPEnd || op == EMcMsgRefreshEnd || op == EMcMsgUSBMassStorageEnd )
                {
                MPX_DEBUG1( "CMPXAddTracksDialog::DoHandleCollectionMessageL Library updated, reloading" );
                // library updated, reload
                LoadCategoriesL();
                }
            else if ( op == EMcMsgDiskRemoved )
                {
                // since collection view can't close this dialog due to CMPXCollectionViewImp::DoIncrementalOpenL
                // later, schedule the dialog to be closed by itself
                iIdle = CIdle::NewL( CActive::EPriorityStandard );
                iIdle->Start( TCallBack( CMPXAddTracksDialog::DeferredExitCallback, this ) );
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// From MMPXCollectionObserver
// Handle collection message
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleCollectionMessage(
    CMPXMessage* aMessage, TInt aError )
    {
    if ( aError == KErrNone && aMessage )
        {
        TRAP_IGNORE( DoHandleCollectionMessageL( *aMessage ) );
        }
    }

// ---------------------------------------------------------------------------
// From MMPXCollectionObserver
// Handles the collection entries being opened. Typically called
// when client has Open()'d a folder
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleOpenL(
    const CMPXMedia& /*aEntries*/,
    TInt /*aIndex*/,
    TBool /*aComplete*/,
    TInt /*aError*/ )
    {
    // do nothing
    }

// ---------------------------------------------------------------------------
// From MMPXCollectionObserver
// Handles the collection entries being opened. Typically called
// when client has Open()'d an item. Client typically responds by
// 'playing' the item
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleOpenL(
    const CMPXCollectionPlaylist& /*aPlaylist*/,
    TInt /*aError*/ )
    {
    // do nothing
    }

// ---------------------------------------------------------------------------
// From MMPXCollectionObserver
// Handle collection media
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleCollectionMediaL(
    const CMPXMedia& aMedia, TInt aError)
    {
    MPX_FUNC( "CMPXAddTracksDialog::HandleCollectionMediaL" );

    if ( aError == KErrNone )
        {
        iPlaylistId = aMedia.ValueTObjectL<TMPXItemId>( KMPXMediaGeneralId );
        MPX_DEBUG2( "CMPXAddTracksDialog::HandleCollectionMediaL playlist ID = 0x%x", iPlaylistId.iId1 );
        }
    }

// ---------------------------------------------------------------------------
// From MMPXCollectionFindObserver
// Handle callback for "find" operation
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleFindAllL(
    const CMPXMedia& aResults, TBool /*aComplete*/, TInt aError )
    {
    MPX_DEBUG2( "-->CMPXAddTracksDialog::HandleFindAllL aError = %d", aError );

    if ( aError == KErrNone )
        {
        switch ( iCurrentOp )
            {
            case EMPXOpGetCategory:
                {
                UpdateListboxByCategoryL( aResults );
                CMPXMedia* media = CMPXMedia::NewL();
                CleanupStack::PushL( media );
                media->SetTObjectValueL<TMPXGeneralType>(
                    KMPXMediaGeneralType, EMPXGroup );
                media->SetTObjectValueL<TMPXGeneralCategory>(
                    KMPXMediaGeneralCategory, EMPXSong );
                RArray<TMPXAttribute> attrs;
                CleanupClosePushL( attrs );
                attrs.Append(
                    TMPXAttribute( KMPXMediaIdGeneral,
                        EMPXMediaGeneralTitle | EMPXMediaGeneralId
                        | EMPXMediaGeneralType |  EMPXMediaGeneralCategory
                        | EMPXMediaGeneralFlags ) );
                iCurrentOp = EMPXOpGetAllSongs;
                iCollectionUtility->Collection().FindAllL( *media, attrs.Array(), *this );
                CleanupStack::PopAndDestroy( &attrs );
                CleanupStack::PopAndDestroy( media );
                break;
                }
            case EMPXOpGetAllSongs:
                {
                delete iAllSongsArray;
                iAllSongsArray = NULL;
                CMPXMediaArray* mediaAry(
                    const_cast<CMPXMediaArray*>( aResults.Value<CMPXMediaArray>(
                        KMPXMediaArrayContents ) ) );
                User::LeaveIfNull( mediaAry );
                iAllSongsArray = CMPXMediaArray::NewL( *mediaAry );
                iCurrentOp = EMPXOpIdle;
                if ( iExpandAllSongs )
                    {
                    iCommonUiHelper->DismissWaitNoteL();
                    iDoFind = EFalse;
                    ExpandCurrentGroupL();
                    }
                else if ( iDoFind )
                    {
                    iCommonUiHelper->DismissWaitNoteL();
                    iDoFind = EFalse;
                    DoFindL();
                    }
                break;
                }
            case EMPXOpGetSongs:
                {
                CMPXMediaArray* media =
                    const_cast<CMPXMediaArray*>(aResults.Value<CMPXMediaArray>(
                        KMPXMediaArrayContents ) );
                User::LeaveIfNull( media );
                iListModel->InsertTracksUnderGroupL( *media, iCurrentGroupIndex );

                TInt diff = iListModel->OldestExpandedGroupIndex() - iCurrentGroupIndex;
                TInt newCurrentIdx = iListBox->CurrentItemIndex() - diff;
                if ( newCurrentIdx < 0 )
                    {
                    newCurrentIdx = 0;
                    }
                TInt newTopIdx = iListBox->TopItemIndex() - diff;
                if ( newTopIdx < 0 )
                    {
                    newTopIdx = 0;
                    }
                iListBox->SetTopItemIndex( newTopIdx );
                iListBox->SetCurrentItemIndex( newCurrentIdx );
                HandleItemArrayChangeL();
                HighlightListItem( iCurrentGroupIndex );
                UpdateSoftkeyL();
                iCurrentOp = EMPXOpIdle;
                iCommonUiHelper->DismissWaitNoteL();
                break;
                }
            default:
                {
                iCurrentOp = EMPXOpIdle;
                break;
                }
            }
        }
    MPX_DEBUG1( "<--CMPXAddTracksDialog::HandleFindAllL" );
    }

// ---------------------------------------------------------------------------
// From MMPXCHelperObserver
// Handles the completion of adding a playlist event
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleOperationCompleteL( TCHelperOperation /*aOperation*/,
                                                    TInt aError,
                                                    void* aArgument )
    {
    MPX_DEBUG2("-->CMPXAddTracksDialog::HandleOperationCompleteL error = %d", aError);
    if ( aError != KErrNone )
        {
        StopAnimL();
        // Fix for err EJKG-7CFD89 & EJKG-7DZGGL 
        if (aError != KErrNotReady && aError != KErrArgument )
            {
            iCommonUiHelper->HandleErrorL( aError );
            }
        }
    
    CMPXMedia* media = (CMPXMedia*) aArgument;
    delete media;
    MPX_DEBUG1("<--CMPXAddTracksDialog::HandleOperationCompleteL");
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::PreLayoutDynInitL()
// Prepare for layout.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CMPXAddTracksDialog::PreLayoutDynInitL()
    {
    MPX_FUNC( "CMPXAddTracksDialog::PreLayoutDynInitL" );
    // Create context icon
    CreateContextIconL();

    // Backup previous status pane
    BackupPreviousStatusPaneL();

    // Set new title pane
    HBufC* title = StringLoader::LoadLC( R_MPX_CUI_ADDSONGS_TITLE );
    iTitlePane->SetTextL( *title );
    CleanupStack::PopAndDestroy( title );

    // Set find type
    CAknSelectionListDialog::PreLayoutDynInitL();

    // Setup listbox content
    iListBox = static_cast<CEikTextListBox*>( Control( ESelectionListControl ) );
    CAknFilteredTextListBoxModel*
    lbxModel = static_cast<CAknFilteredTextListBoxModel*>( iListBox->Model() );
    iLbxFilterItems = lbxModel->Filter();

    // Initialize last find text
    iLastFindTxt = HBufC::NewL( 0 );

    // Create tracks list model & listbox array
    iListModel = CMPXAddTracksListModel::NewL();
    CMPXAddTracksLbxArray* lbxArray = CMPXAddTracksLbxArray::NewL( iListModel );

    // Set item array to listbox model, transfer ownership
    CTextListBoxModel* listboxModel = iListBox->Model();
    listboxModel->SetItemTextArray( lbxArray );
    listboxModel->SetOwnershipType( ELbmOwnsItemArray );

    // Create and set icon array. Ownership transferred.
    CArrayPtr<CGulIcon>* icons = lbxArray->CreateIconArrayL();
    SetIconArrayL( icons );

    // Set scrollbar
    iListBox->CreateScrollBarFrameL( ETrue );
    iListBox->ScrollBarFrame()->SetScrollBarVisibilityL( CEikScrollBarFrame::EOff,
                                                        CEikScrollBarFrame::EAuto );

    // Enable Marquee
    static_cast<CEikColumnListBox*>( iListBox )->EnableExtendedDrawingL();
    static_cast<CEikColumnListBox*>( iListBox )->ItemDrawer()->
                                                ColumnData()->EnableMarqueeL( ETrue );

    // Enable speed scrolling
    iLbxExtFeat = CMPXLbxExtendedFeatures::NewL( iListBox, EFalse );
    iLbxExtFeat->EnableSpeedScrollL( ETrue );

    CEikStatusPane* statusPane = iAvkonAppUi->StatusPane();

    // Animation icon timer
    iPeriodic = CPeriodic::NewL( CActive::EPriorityIdle );

    // Set empty text
    iListBox->View()->SetListEmptyTextL( *iNoSongText );
    iListModel->SetFindSongsFlag( ETrue );

    // get current playlist ID
    CMPXCollectionPath* cpath = iCollectionUtility->Collection().PathL();
    CleanupStack::PushL( cpath );
    cpath->Back();
    MPX_DEBUG_PATH( *cpath );
    RArray<TMPXAttribute> attrs;
    CleanupClosePushL(attrs);
    attrs.Append( KMPXMediaGeneralId );
    iCollectionUtility->Collection().MediaL( *cpath, attrs.Array() );
    CleanupStack::PopAndDestroy( &attrs );
    CleanupStack::PopAndDestroy( cpath );

    // Update listbox
    LoadCategoriesL();

    // Update softkey
    UpdateSoftkeyL();
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::OkToExitL
// Check if it's ok to exit.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CMPXAddTracksDialog::OkToExitL( TInt aButtonId )
    {
    MPX_FUNC( "CMPXAddTracksDialog::OkToExitL" );
    MPX_DEBUG2( "CMPXAddTracksDialog::OkToExitL aButtonId = 0x%x", aButtonId );
    TBool retVal = EFalse;

    switch (aButtonId)
        {
        case EMPXCmdAddTracksExpand:
            {
            ExpandCurrentGroupL();
            break;
            }
        case EMPXCmdAddTracksCollapse:
            {
            CollapseCurrentGroupL();
            break;
            }
        case EMPXCmdAddTracksAdd:
            {
            // Add track to the playlist
            AddCurrentTrackToPlaylistL();
            break;
            }
        case EMPXCmdAddTracksDone:
            {
            retVal = ETrue;
            break;
            }
        case EMPXCmdAddTracksFindCancel:
            {
            CAknSearchField* findBox = FindBox();
            findBox->MakeVisible( EFalse );
            findBox->SetFocus( EFalse );
            TRAP_IGNORE( findBox->ResetL() );
            iListBox->SetFocus( ETrue );
            
            CAknFilteredTextListBoxModel* lbxModel =
                static_cast<CAknFilteredTextListBoxModel*>( iListBox->Model() );
            lbxModel->Filter()->SetSearchField( findBox );// for laylout of list
            TRect mainPane;
            AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EMainPane, mainPane );
            SetSizeAndPosition( mainPane.Size() );
            DrawDeferred();
            break;
            }
        default:
            {
            retVal = CAknSelectionListDialog::OkToExitL( aButtonId );
            break;
            }
        }

    return retVal;
    }

// -----------------------------------------------------------------------------
// CMPXAddTracksDialog::OfferKeyEventL
// Handles key-events.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TKeyResponse CMPXAddTracksDialog::OfferKeyEventL(
    const TKeyEvent& aKeyEvent,
    TEventCode aType )
    {
    MPX_FUNC( "CMPXAddTracksDialog::OfferKeyEventL" );
    TKeyResponse res = EKeyWasNotConsumed;
    TBool updateCBA = EFalse;
    CAknSearchField* findbox = FindBox();
    
    CAknFilteredTextListBoxModel* lbxModel =
        static_cast<CAknFilteredTextListBoxModel*>( iListBox->Model() );
    lbxModel->Filter()->SetSearchField( NULL );//for find the items

    if ( aType == EEventKey )
        {
        StopAnimL();

        switch ( aKeyEvent.iCode )
            {
            case EKeyUpArrow:
            case EKeyDownArrow:
                {
                updateCBA = ETrue;
                break;
                }
            case EKeyLeftArrow:
                {
                TInt index = CurrentLbxItemIndex();
                if ( index != KErrNotFound )
                    {
                    if ( iListModel->TrackTypeL( index ) != EMPXGroup )
                        {
                        GotoClosestGroupL();
                        }
                    else
                        {
                        CollapseCurrentGroupL();
                        }
                    }
                res = EKeyWasConsumed;
                break;
                }
            case EKeyRightArrow:
                {
                ExpandCurrentGroupL();
                res = EKeyWasConsumed;
                break;
                }
            case EKeyOK:
            case EKeyEnter:
                {
                // Do the right thing on right item
                TInt index = CurrentLbxItemIndex();
                if ( index != KErrNotFound )
                    {
                    if ( iListModel->TrackTypeL( index ) == EMPXGroup )
                        {
                        if ( iListModel->IsExpandedIndex( index ) )
                            {
                            CollapseCurrentGroupL();
                            }
                        else
                            {
                            ExpandCurrentGroupL();
                            }
                        }
                    else
                        {
                        AddCurrentTrackToPlaylistL();
                        }
                    }
                res = EKeyWasConsumed;
                break;
                }
            default:
                {
                break;
                }
            }
        }
    TBool offerEventToBaseClass( EFalse );

    if ( res == EKeyWasNotConsumed )
        {
        if ( ( aKeyEvent.iCode == EKeyUpArrow ) ||
            ( aKeyEvent.iScanCode == EStdKeyUpArrow ) ||
            ( aKeyEvent.iCode == EKeyDownArrow ) ||
            ( aKeyEvent.iScanCode == EStdKeyDownArrow ) )
            {
            res = iLbxExtFeat->HandleLbxKeyEventL( aKeyEvent, aType );
            }
        // handle right soft key
        else if( findbox->IsVisible() &&
            ( aKeyEvent.iCode == EKeyDevice1 ||
              aKeyEvent.iScanCode == EStdKeyDevice1 ) )
            {
            StopAnimL();
            res = EKeyWasConsumed;
            }
        else
            {
            if ( findbox )
                {
                TName text;
                findbox->GetSearchText( text );
                updateCBA = ETrue;
                if ( *iLastFindTxt != text )
                    {
                    // Update CBA if find text is changed
                    delete iLastFindTxt;
                    iLastFindTxt = NULL;
                    iLastFindTxt = text.AllocL();
                    DoFindL();
                    }
                if ( iLastFindTxt->Length() == 0 && aKeyEvent.iCode == EKeyBackspace )
                    {
                    CAknSearchField* findBox = FindBox();
                    findBox->MakeVisible( EFalse );
                    findBox->SetFocus( EFalse );
                    TRAP_IGNORE( findBox->ResetL() );
                    iListBox->SetFocus( ETrue );
                    
                    lbxModel->Filter()->SetSearchField( findBox );// for laylout of list
                    
                    TRect mainPane;
                    AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EMainPane, mainPane );
                    SetSizeAndPosition( mainPane.Size() );
                    iListBox->DrawDeferred();
                    res = EKeyWasConsumed;
                    }
                else
                    {
                    offerEventToBaseClass = ETrue;
                    }
                }
            else
                {
                offerEventToBaseClass = ETrue;
                }
            }
        }
    if ( updateCBA )
        {
        UpdateSoftkeyL();
        }
    if ( offerEventToBaseClass )
        {
        // this has to be done after cba is updated
        res = CAknSelectionListDialog::OfferKeyEventL( aKeyEvent, aType );
        }
    return res;
    }

// ---------------------------------------------------------------------------
// From CCoeControl
// Handles a change to the control's resources.
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleResourceChange( TInt aType )
    {
    MPX_FUNC( "CMPXAddTracksDialog::HandleResourceChange" );
    CAknSelectionListDialog::HandleResourceChange( aType );

    if ( aType == KEikDynamicLayoutVariantSwitch )
        {
        CAknSearchField* findBox = FindBox();    
        CAknFilteredTextListBoxModel* lbxModel =
            static_cast<CAknFilteredTextListBoxModel*>( iListBox->Model() );
        lbxModel->Filter()->SetSearchField( findBox );// for laylout of list

        TRect mainPane;
        AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EMainPane, mainPane );
        SetSizeAndPosition( mainPane.Size() );
        }

    if ( aType == KAknsMessageSkinChange )
        {
        // Create and set icon array. Ownership transferred.
        CTextListBoxModel* listboxModel = iListBox->Model();
        MDesCArray* lbxArray = listboxModel->ItemTextArray();
        CMPXAddTracksLbxArray* addtracksLbxArray =
            static_cast<CMPXAddTracksLbxArray*>( lbxArray );
        TRAP_IGNORE(
            CArrayPtr<CGulIcon>* icons = addtracksLbxArray->CreateIconArrayL();
            SetIconArrayL( icons );
            );
        }
    }

// ---------------------------------------------------------------------------
// From MEikListBoxObserver
// Handles listbox events.
// ---------------------------------------------------------------------------
//
void CMPXAddTracksDialog::HandleListBoxEventL(
    CEikListBox* /*aListBox*/,
    TListBoxEvent aEventType)
    {
    MPX_FUNC( "CMPXAddTracksDialog::HandleListBoxEventL" );
    StopAnimL( EFalse );
    if ( aEventType == EEventEnterKeyPressed || aEventType == EEventItemDoubleClicked
#ifdef SINGLE_CLICK_INCLUDED
         || aEventType == EEventItemSingleClicked
#endif
        )
        {
		// Do the right thing on right item
		TInt index = CurrentLbxItemIndex();
		if ( index != KErrNotFound )
			{
			if ( iListModel->TrackTypeL( index ) == EMPXGroup )
				{
				if ( iListModel->IsExpandedIndex( index ) )
					{
					CollapseCurrentGroupL();
					}
				else
					{
					ExpandCurrentGroupL();
					}
				}
			else
				{
				AddCurrentTrackToPlaylistL();
				}
			}
        }
    else
	    {
        UpdateSoftkeyL();
	    }
    }

// ---------------------------------------------------------------------------
// Function called by CIdle to close the dialog itself when collection view
// can not handle the closing of dialog due to waitnote displayed by
// CMPXCollectionViewImp::DoIncrementalOpenL
// ---------------------------------------------------------------------------
//
TInt CMPXAddTracksDialog::DeferredExitCallback( TAny* aPtr )
    { 
    MPX_DEBUG1( "CMPXAddTracksDialog::DeferredExitCallback" );  
    CMPXAddTracksDialog* self = 
        static_cast<CMPXAddTracksDialog*>( aPtr );
    TRAP_IGNORE( self->TryExitL( EMPXCmdAddTracksDone ) );
    return KErrNone;
    }

// ProcessCommandL has been implemented here for the sole
// of handling RSK command when findbox is shown. For other
// cases it goes along the same path that is used by hardware
// keys, e.g. by call to TryToExitL.
void CMPXAddTracksDialog::ProcessCommandL( TInt aCommandId )
	{
	CAknSelectionListDialog::ProcessCommandL( aCommandId );
	TryExitL( aCommandId );
	
	// Check that the dialog is not being closed
	if( aCommandId != EMPXCmdAddTracksDone )
		{
		// Reset view so that all songs are shown
		TName text;
		CAknSearchField* findbox = FindBox();
		if( findbox )
			{
			findbox->GetSearchText( text );
			delete iLastFindTxt;
			iLastFindTxt = NULL;
			iLastFindTxt = text.AllocL();
			DoFindL();
			HandleItemArrayChangeL();
			}
		}
	}
//  End of File
