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


// INCLUDE FILES

#include "easydialinglistbox.h"
#include "easydialinglistboxview.h"
#include "easydialinglistboxitemdrawer.h"
#include "easydialinglistboxdata.h"
#include "easydialingpanics.h"
#include "easydialingcontactdatamanager.h"
#include "easydialingcrkeys.h"

#include <easydialingpluginresources.rsg>

#include <phoneappcommands.hrh>
#include <phonebook2.mbg>
#include <phonebook2ece.mbg>

#include <ccappcommlauncherpluginrsc.rsg>

#include <aknlongtapdetector.h> // Required for touch
#include <AknsFrameBackgroundControlContext.h>

#include <aknlayoutscalable_apps.cdl.h>
#include <layoutmetadata.cdl.h>
#include <gulicon.h>
#include <centralrepository.h>
#include <bautils.h>    // for BaflUtils

#include <aknlistloadertfx.h>
#include <aknlistboxtfxinternal.h>
#include <aknlistboxtfx.h>

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES  

extern TRect ContactImageBoundingBox( const TRect& aItemRect );
extern TRect ActionMenuIconBoundingBox(const TRect& aItemRect);

// CONSTANTS
static const TInt KListBoxMarginWidth = 2;

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS


/*
 * ==============================================================================
 * 
 * 
 * class CEasyDialingListBox
 * 
 * 
 * ==============================================================================
 */


// -----------------------------------------------------------------------------
// CEasyDialingListBox
// 
// -----------------------------------------------------------------------------
//
CEasyDialingListBox::CEasyDialingListBox() :
    CEikFormattedCellListBox()
    {
    }

// -----------------------------------------------------------------------------
// ~CEasyDialingListBox
// 
// -----------------------------------------------------------------------------
//
CEasyDialingListBox::~CEasyDialingListBox()
    {
    delete iBGContext;
    delete iLongTapDetector;
    }

// -----------------------------------------------------------------------------
// ConstructL
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::ConstructL( TInt aFlags, 
        CEasyDialingContactDataManager* aContactDataManager )
    {
    CEikFormattedCellListBox::ConstructL( NULL, aFlags );
    
    iLongTapDetector = CAknLongTapDetector::NewL( this );
 
    CEasyDialingListBoxItemDrawer* drawer = static_cast<CEasyDialingListBoxItemDrawer*> (iItemDrawer);
    CEasyDialingListBoxData* data = drawer->EasyDialingCellData();
    data->SetContactDataManager(aContactDataManager);
    data->SetEDLBXControl(this);
    
    iBGContext = CAknsFrameBackgroundControlContext::NewL( KAknsIIDQsnFrPopupSub,
            Rect(), Rect(), EFalse );
    
    // ContactDataManager is accessed from SizeChanged. If list item size
    // changes -> thumbnail size changes
    iContactDataManager = aContactDataManager;
    
    drawer->SetColors();

    CreateScrollBarFrameL( ETrue );
    ScrollBarFrame()->SetScrollBarVisibilityL( CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto );
    }


// -----------------------------------------------------------------------------
// MakeViewClassInstanceL
// 
// -----------------------------------------------------------------------------
//
CListBoxView* CEasyDialingListBox::MakeViewClassInstanceL()
    {
    return (new ( ELeave ) CEasyDialingListBoxView);
    }


// -----------------------------------------------------------------------------
// ItemDrawer
// 
// -----------------------------------------------------------------------------
//
CEasyDialingListBoxItemDrawer* CEasyDialingListBox::ItemDrawer() const
    {
    return (CEasyDialingListBoxItemDrawer*) iItemDrawer;
    }


// -----------------------------------------------------------------------------
// FocusChanged
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::FocusChanged( TDrawNow aDrawNow )
    {
    if ( !IsFocused() )
        {
        CEasyDialingListBoxView* view = static_cast<CEasyDialingListBoxView*>( iView );
        view->SetCurrentItemIndexToNone();
        }
    CEikFormattedCellListBox::FocusChanged( aDrawNow );
    }


// -----------------------------------------------------------------------------
// CreateItemDrawerL
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::CreateItemDrawerL()
    {
    CEasyDialingListBoxData* celldata = CEasyDialingListBoxData::NewL( *iEikonEnv );
    CleanupStack::PushL( celldata );
    iItemDrawer = new(ELeave) CEasyDialingListBoxItemDrawer(Model(), iEikonEnv->NormalFont(), celldata);
    CleanupStack::Pop( celldata );  
    }


// -----------------------------------------------------------------------------
// Draw
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::Draw(const TRect& aRect) const
    {
    MAknListBoxTfxInternal *transApi = CAknListLoader::TfxApiInternal( iItemDrawer->Gc() );
    if ( transApi )
        {
        transApi->SetListType( MAknListBoxTfxInternal::EListBoxTypeMainPane );
        transApi->BeginRedraw( MAknListBoxTfxInternal::EListView, this->Rect() );
        }
    
    if (!iView->RedrawDisabled() )
        {
        TRect clientRect( Rect() );
        TRect viewRect = iView->ViewRect();
        
        if ( transApi )
            {
            transApi->StartDrawing( MAknListBoxTfxInternal::EListView );
            }

        AknsDrawUtils::BackgroundBetweenRects( 
            AknsUtils::SkinInstance(), 
            iBGContext, 
            this, 
            *iItemDrawer->Gc(), 
            clientRect, 
            viewRect );
        
        if ( transApi )
            {
            transApi->StopDrawing();
            }
        }
    
    if ( iModel->NumberOfItems() )
        {
        // Draw the actual list
        iView->Draw(&aRect);
        }
    if ( transApi )
        {
        transApi->EndViewRedraw( aRect );
        }
    }


// -----------------------------------------------------------------------------
// HandleResourceChange
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::HandleResourceChange(TInt aType)
    {
    CEikFormattedCellListBox::HandleResourceChange( aType );
    iSBFrame->VerticalScrollBar()->HandleResourceChange( aType );
    iSBFrame->DrawScrollBarsNow();
    ItemDrawer()->SetColors();
    
    // Base call to HandleResourceChange justifies the list to even items
    // on the top. This causes problems when listbox height is not a multiple of
    // listitem heights. Fix scrolling manually for these cases.
    TRAP_IGNORE( HandleItemAdditionL() ); // needed to prevent drawing problems
    TInt scrollIndex = ( IsFocused() ? CurrentItemIndex() : iNumberOfNames - 1 );
    ScrollToMakeItemVisible( scrollIndex );
    
    DrawDeferred();
    }


// -----------------------------------------------------------------------------
// SizeChanged
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::SizeChanged()
    {
	// Note that within this call view rect is adjusted to fit into TGulBorder
    // that could have been given in construction. It's not used by ED listbox.
    // The view rect is not correct for ED after this call but it's
    // calculated and set below.
    CEikFormattedCellListBox::SizeChanged();
    
    TRect parentRect = Rect();
    
    // Set the listbox colors.
    // For some reason, calling this in HandleResourceChange is not enough, it does
    // not get called in situation it should.
    ItemDrawer()->SetColors();
    
    // Get all the layout rects
    TAknLayoutRect rect;
    TInt variety = ( Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0 );

    TAknWindowComponentLayout viewLayout( AknLayoutScalable_Apps::dia3_list_pane( variety ) );
    rect.LayoutRect(parentRect, viewLayout );
    TRect viewRect( rect.Rect() );
    // Add a bit of margin around the view as layout doesn't define any
    viewRect.Shrink( KListBoxMarginWidth, KListBoxMarginWidth );
    
    TAknWindowComponentLayout scrollBarLayout( AknLayoutScalable_Apps::scroll_pane_cp12() );
    rect.LayoutRect( parentRect, scrollBarLayout );
    TRect scrollBarRect( rect.Rect() );

    TRect viewAndScrollBarRect( viewRect );
    if ( AknLayoutUtils::LayoutMirrored() )
        {
        viewAndScrollBarRect.iTl.iX = scrollBarRect.iTl.iX + KListBoxMarginWidth;
        }
    else
        {
        viewAndScrollBarRect.iBr.iX = scrollBarRect.iBr.iX - KListBoxMarginWidth;
        }

    // If scrollbar is not needed, then we can use all the space for the list view
    if ( GetHeightBasedOnNumberOfItems( iNumberOfNames ) <= parentRect.Height() )
        {
        viewRect = viewAndScrollBarRect;
        scrollBarRect.SetWidth( 0 );
        }

    // Must use this method so that all eiklistbox's internal variables are 
    // set and updated according to this viewRect. This call would
    // add list margins but no such are defined in layouts so view's rect is
    // set to viewRect calculated here.
    SetViewRectFromClientRect( viewRect );
    
    // Set scroll bar rect
    if ( iSBFrame )
        {
        CAknDoubleSpanScrollBar* scrollbar = static_cast <CAknDoubleSpanScrollBar*>( iSBFrame->VerticalScrollBar() );
        scrollbar->SetFixedLayoutRect( scrollBarRect );
        scrollbar->SetRect( scrollBarRect );
        scrollbar->MakeVisible( scrollBarRect.Width() != 0 );
        }

    iBGContext->SetFrameRects( parentRect, viewAndScrollBarRect );
    
    TRAP_IGNORE( CEikFormattedCellListBox::HandleViewRectSizeChangeL() );
    }


// -----------------------------------------------------------------------------
// MopSupplyObject
// 
// -----------------------------------------------------------------------------
//
TTypeUid::Ptr CEasyDialingListBox::MopSupplyObject( TTypeUid aId )
    {
    if ( aId.iUid == MAknsControlContext::ETypeId && iBGContext )
        {
        return MAknsControlContext::SupplyMopObject( aId, iBGContext );
        }
    return CEikFormattedCellListBox::MopSupplyObject( aId );    
    }

// -----------------------------------------------------------------------------
// MakeVisible
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::MakeVisible( TBool aVisible )
    {
    if ( aVisible != IsVisible() )
        {
        CEikFormattedCellListBox::MakeVisible( aVisible );
        if ( iSBFrame )
            {
            CEikScrollBar* sb = iSBFrame->GetScrollBarHandle( CEikScrollBar::EVertical );
            if ( sb )
                {
                sb->MakeVisible( aVisible );
                if ( aVisible )
                    {
                    UpdateScrollBarThumbs();
                    }
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// SetMaxRect
//
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::SetMaxRect( TRect aMaxRect )
    {
    iMaxRect = aMaxRect;

    // Update the list item size according the maximum view rect size
    TInt variety = ( Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0 );
    TAknLayoutRect maxViewLayout;
    maxViewLayout.LayoutRect( aMaxRect, AknLayoutScalable_Apps::dia3_list_pane( variety ) );
    TRect maxViewRect( maxViewLayout.Rect() );
    maxViewRect.Shrink( KListBoxMarginWidth, KListBoxMarginWidth ); // layout data doens't include any margins but we have added some

    // Read list item size aid from layout data. Use it to determine how many
    // items to show in listbox.
    TAknLayoutRect listSizeAid;
    listSizeAid.LayoutRect( aMaxRect, AknLayoutScalable_Apps::aid_size_list_single_double() );
    TInt itemHeightAid = listSizeAid.Rect().Height();
    
    TInt maxItemsShown = maxViewRect.Height() / itemHeightAid;
    if ( maxViewRect.Height() % itemHeightAid > itemHeightAid / 2 )
        {
        maxItemsShown++;
        }
    
    TInt itemHeight = maxViewRect.Height() / maxItemsShown;
    TRAP_IGNORE( SetItemHeightL( itemHeight ) );

    // Calculate new thumbnail rect from item size
    TSize itemSize( maxViewRect.Width(), itemHeight );
    TRect thumbnailRect = ContactImageBoundingBox( itemSize );
    iContactDataManager->SetThumbnailSize( thumbnailRect.Size() ); // reloads thumbnails if the new size is different from old one

    // Inform listbox data class about changed list item size
    ItemDrawer()->EasyDialingCellData()->HandleItemSizeChange();
    }

// -----------------------------------------------------------------------------
// SetRectToNumberOfItems
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::SetRectToNumberOfItems( TInt aNumberOfNames ) 
    {
    iNumberOfNames = aNumberOfNames;

    TRect listboxRect( iMaxRect );
    
    // Make listbox smaller if maximum size is not needed to show
    // all list items.
    TInt requiredHeight = GetHeightBasedOnNumberOfItems( iNumberOfNames );
    if ( requiredHeight < listboxRect.Height() )
        {
        // Shrink rect height so that bottom of the rect doesn't move
        listboxRect.iTl.iY += ( listboxRect.Height() - requiredHeight );
        }
    
    SetRect( listboxRect );
    }


// -----------------------------------------------------------------------------
// SetFocusedWithKeyEventL
// -----------------------------------------------------------------------------
//
TKeyResponse CEasyDialingListBox::SetFocusedWithKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType )
    {
    TKeyResponse keyResponse = EKeyWasNotConsumed;
    TInt keyCode = aKeyEvent.iCode;
            
    // Disable redrawing temporarily.
    // This is because OfferKeyEventL puts the focus to the wrong item,
    // and we don't want to show it flashing there.
    // Make sure that no leave happens before redraw is enabled again.
    iView->SetDisableRedraw( ETrue );
        
    TRAP_IGNORE( keyResponse = OfferKeyEventL( aKeyEvent, aType ) );
    
    TInt itemToFocus = ( keyCode == EKeyUpArrow ? iNumberOfNames - 1 : 0 );
    ScrollToMakeItemVisible( itemToFocus );
    SetCurrentItemIndex( itemToFocus );
        
    iView->SetDisableRedraw( EFalse );
    
    DrawDeferred();
    
    return keyResponse;
    }


// -----------------------------------------------------------------------------
// GetHeightBasedOnNumberOfItems
// 
// -----------------------------------------------------------------------------
//
TInt CEasyDialingListBox::GetHeightBasedOnNumberOfItems( TInt aNum ) const
    {
    return ( ItemHeight()*aNum + KListBoxMarginWidth*2 ); 
    }


// -----------------------------------------------------------------------------
// CurrentContactDataIndex
// 
// -----------------------------------------------------------------------------
//
TInt CEasyDialingListBox::CurrentContactDataIndex()
    {
    if ( CurrentItemIndex() >= 0 ) 
        {
        TPtrC itemText = Model()->ItemText( CurrentItemIndex() );
        TPtrC indexText;
        
        TInt error = TextUtils::ColumnText( indexText , 0, &itemText );
        __ASSERT_DEBUG( error == KErrNone, EasyDialingPanic( EEasyDialingPanicInvalidListBoxModelString ) );

        TUint id( 0 );
        TLex lex( indexText );
        error = lex.Val( id, EHex );
        __ASSERT_DEBUG( error == KErrNone, EasyDialingPanic( EEasyDialingPanicInvalidListBoxModelString ) );
        
        return id;
        }
    else 
        {
        return KErrNotFound;
        }
    }


// -----------------------------------------------------------------------------
// CurrentContactLinkL
// 
// -----------------------------------------------------------------------------
//
HBufC8* CEasyDialingListBox::CurrentContactLinkL()
    {
    TInt index = CurrentContactDataIndex();
    if ( index >= 0 )
        {
        HBufC8* packedContact = iContactDataManager->ContactLinkLC( index );
        CleanupStack::Pop( packedContact );
        return packedContact;
        }
    else
        {
        return NULL;
        }
    }


// -----------------------------------------------------------------------------
// HandleLongTapEventL
// Callback from CAknLongTapDetector.
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::HandleLongTapEventL( const TPoint& /*aPenEventLocation*/, 
                                               const TPoint& /*aPenEventScreenLocation*/ )
    {
    ReportListBoxEventL( static_cast<MEikListBoxObserver::TListBoxEvent>
            ( KEasyDialingContactLongTapped ) );
    iPointerLongPressHandled = ETrue;
    
    TPointerEvent simulatedButtonUpEvent( TPointerEvent::EButton1Up, 0,
                                          TPoint(), TPoint() );
    TRAP_IGNORE( CEikFormattedCellListBox::HandlePointerEventL( simulatedButtonUpEvent ) );
    }


// -----------------------------------------------------------------------------
// HandlePointerEventL
// Pointer event handling within the EasyDialingListBox.
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::HandlePointerEventL(const TPointerEvent& aPointerEvent)
    {
    // Button down event starts new pointer press. Reset flags at this point.
    if ( aPointerEvent.iType == TPointerEvent::EButton1Down )
        {
        iPointerLongPressHandled = EFalse;
        }
    
    // check if pointer event is over item
    TInt itemIndex( KErrNotFound );
    TPoint pointerPos( aPointerEvent.iPosition );
    TBool pointerIsOverAnItem = iView->XYPosToItemIndex( pointerPos, itemIndex );
    
    // long tap is started only if pointer is over an actual item
    // (not over margin or other empty listbox area)
    if ( pointerIsOverAnItem || aPointerEvent.iType != TPointerEvent::EButton1Down )
        {
        iLongTapDetector->PointerEventL(aPointerEvent);
        }
    
    if ( iPointerLongPressHandled )
        {
        // No further handling is made after long tap on list item
        // has been detected.
        return;
        }

    CEikFormattedCellListBox::HandlePointerEventL( aPointerEvent );
    }


// -----------------------------------------------------------------------------
// HandleScrollEventL
// If the EasyDialingListBox is scrolled by dragging the Scroll bar, this
// observer function is called
// -----------------------------------------------------------------------------
//
void CEasyDialingListBox::HandleScrollEventL( CEikScrollBar* aScrollBar, TEikScrollEvent aEventType )
    {
    // Report scrolling to the listbox observer
    TInt scrollEvent( KErrNotFound );
    switch ( aEventType )
        {
        case EEikScrollThumbDragVert:
            scrollEvent = KEasyDialingScrollingStarted;
            break;

        case EEikScrollThumbReleaseVert:
        case EEikScrollUp:
        case EEikScrollDown:
        case EEikScrollPageUp:
        case EEikScrollPageDown:
            scrollEvent = KEasyDialingScrollingStopped;
            break;
            
        default:
            break;
        }
    
    if ( scrollEvent != KErrNotFound )
        {
        ReportListBoxEventL( static_cast<MEikListBoxObserver::TListBoxEvent>( scrollEvent ) );
        }
    
    // Base class(es) might be also interested about the scrollbar events
    CEikFormattedCellListBox::HandleScrollEventL( aScrollBar, aEventType );
    }

// end of file

