phoneuis/easydialing/src/easydialinglistboxdata.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 19 Feb 2010 22:50:26 +0200
branchRCL_3
changeset 9 8871b09be73b
child 14 24062c24fe38
permissions -rw-r--r--
Revision: 201003 Kit: 201007

/*
* 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 data.
*
*/


// INCLUDE FILES

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

#include "easydialingcontactdatamanager.h"
#include <easydialing.mbg>
#include <phonebook2.mbg>
#include <phonebook2ece.mbg>

// AvKON and drawing header files
#include <aknlayoutscalable_avkon.cdl.h>
#include <akniconconfig.h>
#include <gulicon.h>
#include <AknBidiTextUtils.h>
#include <bidivisual.h>

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

#include <akntransitionutils.h>
#include <avkon.rsg>


// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES  

// CONSTANTS

// KHighlightSeparatorChar is character used to separate matching and non-matching
// portions of contact names. 
const TInt KHighlightSeparatorChar = 0x1F;
_LIT( KHighlightSeparatorCharAsLit, "\x1F" );


// KContactNameFontHeightPercent is contact name font height relative to list item height.
const TInt KContactNameFontHeightPercent = 35;

// KCompanyNameFontHeightPercent is company name font height relative to list item height.
const TInt KCompanyNameFontHeightPercent = 30;

// KTextBoundingBoxHeightPercent gives the text bounding box height in percentages 
// relative to font height. This must be over 100, or part of the text cuts off.
const TInt KTextBoundingBoxHeightPercent = 120;

// KTextPlacementPercent controls how text is placed vertically within its bounding box. 
// The value is between 0 and 100. 0 means in top part, 50 mean in the middle, 100 means in the
// bottom. 
const TInt KTextPlacementPercent = 70;

// KMarginXPercent defines a width of horizontal margin used in many places. In relation to
// the width of the item rectangle.
const TInt KMarginXPercent = 2;

// KMarginYPercent defines a height of vertical margin. In relation to
// the height of the item rectangle. Currently used only with contact thumbnail.
const TInt KMarginYPercent = 4;

// KContacNameYOffsetPercent defines the vertical placement of contact name in relation to 
// item height.
const TInt KContactNameYOffsetPercent = 10;

// KCompanyNameYOffsetPercent defines the vertical placement of company name in relation to 
// item height.
const TInt KCompanyNameYOffsetPercent = 60;

// KArrowIconSizePercent defines the size of action menu icon relative to item height.
const TInt KArrowIconSizePercent = 20;

// KMatchingTextMarginInPixels the absolute pixel width of highlight margin. Highlight margin
// is an extra space in highlight boundary to make the text look less crowded.
const TInt KMatchingTextMarginInPixels = 3;

// KMatchingTextMarginInPixels is the absolute pixel value for rounding used in highlight 
// rectangle.
const TInt KHighligthRectangleRoundingYInPixels = 4;

// KThumbnailAspectRatio is the aspect ratio of contact thumbnail in percents. 133 for instance
// is 4:3 aspect ration.
const TInt KThumbnailAspectRatio = 133;

const TInt KCent = 100;

const TInt KMaxRunInfoArrayCount = 20;

// IMPLEMENTATION SPECIFIC CONSTANTS
// The mif file from where you would like to show the 
// icon on the screen.
_LIT( KFavouriteIconBitmapFile, "\\resource\\apps\\phonebook2.mif" );
_LIT( KEasyDialingBitmapFile, "\\resource\\apps\\easydialing.mif" );
_LIT( KPhonebook2EceBitmapFile, "\\resource\\apps\\phonebook2ece.mif" );

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// GLOBAL FUNCTION PROTOTYPES
TRect ContactImageBoundingBox( const TRect& aItemRect );
TRect ArrowIconBoundingBox(const TRect& aItemRect);

// LOCAL FUNCTION PROTOTYPES
static TRect ContactNameBoundingBox(
        const TRect& aItemRect, 
        const CFont* aContactNameFont, 
        TBool aIsCurrentItem,
        TBool aIsFavourite, 
        TBool aThumbnailsShown );
static TRect CompanyNameBoundingBox(
        const TRect& aItemRect, 
        const CFont* aCompanyNameFont, 
        TBool aIsCurrentItem,
        TBool aThumbnailsShown );
static TRect FavouriteIconBoundingBox( const TRect& aContactNameBoundingBox, TInt aTextWidth );
static TRect MirrorLayoutBoundingBox(const TRect& aSourceRect, TRect& aBoundingBoxRect);
static TInt BaseLineOffset( const TRect& aTextBoundingBox, const CFont* aFont );
static TBool ContainsRightToLeftText( const TDesC& aDesc );
static TInt HighlightSeparatorCount( const TDesC& aText );
static HBufC* ConvertToVisualAndClipLC( const TDesC& aText, const CFont& aFont, const TRect& aBoundingBox ); 

static void ClipTextToWidth(
        TDes& aText,
        const CFont& aFont,
        TInt aMaxWidthInPixels,
        TBool& aMatch );

static TBool DrawPieceOfText(
        const TRect& aBoundingBox,
        TInt& aXOffset,
        CWindowGc &aGc, 
        const TDesC& aText,
        TBool aMatch,
        const CFont* aFont,
        const CEasyDialingListBoxData::TExtendedColors& aColors,
        TBool aHighLight);

static TInt DrawTextWithMatchHighlightL(
        const TRect& aBoundingBox,
        CWindowGc &aGc, 
        const TDesC& aText,
        const CFont* aFont,
        const CEasyDialingListBoxData::TExtendedColors& aColors,
        TBool aHighLight );

static TInt CalculateTextWidth(
        const TRect& aBoundingBox,
        const TDesC& aText,
        const CFont* aFont );

static TBool CalculateTextPieceWidth(
        const TRect& aBoundingBox,
        TInt& aXOffset,
        const TDesC& aText,
        TBool aMatch,
        const CFont* aFont );

// FORWARD DECLARATIONS


/*
 * ==============================================================================
 * 
 * 
 * class EasyDialingListBoxData::TExtendedColors
 * 
 * 
 * ==============================================================================
 */


// -----------------------------------------------------------------------------
// TExtendedColors
// 
// -----------------------------------------------------------------------------
//       
CEasyDialingListBoxData::TExtendedColors::TExtendedColors() :
CFormattedCellListBoxData::TColors(),
iMatchingText(KRgbBlack),
iMatchingBack(KRgbDarkYellow)
    {
    }


/*
 * ==============================================================================
 * 
 * 
 * class CEasyDialingListBoxData
 * 
 * 
 * ==============================================================================
 */


// -----------------------------------------------------------------------------
// CEasyDialingListBoxData
// 
// -----------------------------------------------------------------------------
//       
CEasyDialingListBoxData::CEasyDialingListBoxData() :
CFormattedCellListBoxData()
    {
    }

// -----------------------------------------------------------------------------
// Destructor
// 
// -----------------------------------------------------------------------------
//       
CEasyDialingListBoxData::~CEasyDialingListBoxData()
    {
    // Release fonts. ReleaseFont function can cope with null pointer
    // so we don't need to null check them.
    CWsScreenDevice& screenDev = *( CEikonEnv::Static()->ScreenDevice() );
    screenDev.ReleaseFont( iContactNameFont );
    screenDev.ReleaseFont( iCompanyNameFont );
    
    delete iArrowPointingRight;
    delete iArrowPointingLeft;
    delete iColorBitmap;
    delete iDummyThumbnail;
    
    iContactDataManager = NULL;
    }

// -----------------------------------------------------------------------------
// NewL
// 
// -----------------------------------------------------------------------------
//       
CEasyDialingListBoxData* CEasyDialingListBoxData::NewL()
    {
    CEasyDialingListBoxData* self = new (ELeave) CEasyDialingListBoxData();
    
    CleanupStack::PushL( self );
    self->ConstructLD();
    CleanupStack::Pop( self );
 
    return self;
    }


// -----------------------------------------------------------------------------
// DrawData
// 
// -----------------------------------------------------------------------------
//       
void CEasyDialingListBoxData::DrawData(
    const TListItemProperties& aProperties, 
    CWindowGc& aGc,
    const TDesC* aText,
    const TRect& aRect,
    TBool aHighlight,
    const TExtendedColors& aColors ) const
    {
    const TRect &aItemRect = aRect;
    
    if ( aHighlight )
        {
        DrawHighlight( aGc, aItemRect );
        }
    
    // Draw the actual items.
    DrawDataFormatted( aProperties, aGc, aText, aItemRect, 
            aHighlight, aColors );
    }


// -----------------------------------------------------------------------------
// ConstructLD
// 
// -----------------------------------------------------------------------------
//       
void CEasyDialingListBoxData::ConstructLD()
    {
    CFormattedCellListBoxData::ConstructLD();
    
    // EasyDialing bitmap file is attempted to be read from the same directory where the
    // executed binary is located
    TFileName dllFileName;
    Dll::FileName( dllFileName );
     
    TParse parse;
    User::LeaveIfError(parse.Set(KEasyDialingBitmapFile, &dllFileName, NULL));
    TFileName bitmapFileName(parse.FullName());
      
    CFbsBitmap* bm;
    CFbsBitmap* mask;
    
    // iArrowPointingLeft is the icon displayed for the selected item in EasyDialingListBox
    AknIconUtils::CreateIconL( bm, mask, bitmapFileName, EMbmEasydialingQgn_indi_org_arrow_left, 
            EMbmEasydialingQgn_indi_org_arrow_left_mask );

    iArrowPointingLeft = CGulIcon::NewL( bm, mask );

    AknIconUtils::CreateIconL( bm, mask, bitmapFileName, EMbmEasydialingQgn_indi_org_arrow_right, 
            EMbmEasydialingQgn_indi_org_arrow_right_mask );

    iArrowPointingRight = CGulIcon::NewL( bm, mask );

    // Only mask for the icons are used. iColorBitmap is used for making the icon 
    // to follow text color changes according to skin.
    iColorBitmap = new (ELeave) CFbsBitmap;

    // Contact default thumbnail is not available in themes. It is read from phonebook resource. 
    AknIconUtils::CreateIconL( bm, mask, KPhonebook2EceBitmapFile,
            EMbmPhonebook2eceQgn_prop_pb_thumb_unknown, EMbmPhonebook2eceQgn_prop_pb_thumb_unknown_mask );
    iDummyThumbnail = CGulIcon::NewL( bm, mask );

    }


// -----------------------------------------------------------------------------
// DrawHighlight
// 
// -----------------------------------------------------------------------------
//       
void CEasyDialingListBoxData::DrawHighlight( CWindowGc &aGc, const TRect &aItemRect ) const
    {
    MAknListBoxTfxInternal* transApi = CAknListLoader::TfxApiInternal( &aGc );
    if ( transApi )
        {
        transApi->Invalidate(MAknListBoxTfxInternal::EListHighlight );
        transApi->BeginRedraw( MAknListBoxTfxInternal::EListHighlight, aItemRect );
        transApi->StartDrawing( MAknListBoxTfxInternal::EListHighlight );
        aGc.SetClippingRect( iControl->Rect() );
        }

    TAknLayoutRect outerRect;
    TAknLayoutRect innerRect;
    outerRect.LayoutRect( aItemRect, TAknWindowComponentLayout::Compose(
            AknLayoutScalable_Avkon::list_highlight_pane_cp1(),
            AknLayoutScalable_Avkon::list_highlight_pane_g10_cp1() ).LayoutLine() );
    innerRect.LayoutRect( aItemRect, TAknWindowComponentLayout::Compose(
            AknLayoutScalable_Avkon::list_highlight_pane_cp1(),
            AknLayoutScalable_Avkon::list_highlight_pane_g1_cp1() ).LayoutLine() );
    MAknsControlContext *cc = AknsDrawUtils::ControlContext( Control() );

    if ( !cc ) 
        {
        cc = SkinBackgroundContext();
        }

    if ( cc )
        {
        aGc.SetPenStyle( CGraphicsContext::ENullPen );
        AknsDrawUtils::DrawFrame(
                AknsUtils::SkinInstance(), 
                aGc,
                outerRect.Rect(),
                innerRect.Rect(),
                KAknsIIDQsnFrList,
                KAknsIIDQsnFrListCenter );
        }

    if ( transApi )
        {
        aGc.CancelClippingRect();
        transApi->StopDrawing();
        transApi->EndRedraw( MAknListBoxTfxInternal::EListHighlight );
        }
    }


// -----------------------------------------------------------------------------
// DrawDataFormatted
// 
// -----------------------------------------------------------------------------
// 
// ToDo: SetSize function could be called once for all static data in SizeChanged.
// Applies at least to icons.
void CEasyDialingListBoxData::DrawDataFormatted( 
        TListItemProperties /* aProperties */,
        CWindowGc& aGc,
        const TDesC* aText,
        const TRect& aItemRect,
        TBool aHighlight,
        const TExtendedColors& aColors ) const
    {
    TPtrC cellText;

    TInt error = TextUtils::ColumnText( cellText , 0, aText );
    __ASSERT_DEBUG( error == KErrNone, EasyDialingPanic( EEasyDialingPanicInvalidListBoxModelString ) );
    __ASSERT_DEBUG( iContactNameFont, EasyDialingPanic( EEasyDialingNoFontFound ) );
    __ASSERT_DEBUG( iCompanyNameFont, EasyDialingPanic( EEasyDialingNoFontFound ) );
    
    MAknListBoxTfxInternal *transApi = CAknListLoader::TfxApiInternal( &aGc );
    if ( transApi )
        {
        transApi->StartDrawing( MAknListBoxTfxInternal::EListItem );
        if ( transApi->EffectsDisabled() )
            {
            aGc.SetClippingRect( iControl->Rect() );
            }
        }

    TRect boundingBox = ContactImageBoundingBox( aItemRect );
    if ( AknLayoutUtils::LayoutMirrored() )
        {
        boundingBox = MirrorLayoutBoundingBox( aItemRect, boundingBox );
        }
    
    //Draws the Contact Thumbnail Icon if exists else draws the dummy contact thumbnail
    TBool fav = ContactThumbnailDrawing( aGc, boundingBox, cellText );   

    error = TextUtils::ColumnText( cellText , 1, aText );
    __ASSERT_DEBUG( error == KErrNone, EasyDialingPanic( EEasyDialingPanicInvalidListBoxModelString ) );

    boundingBox = ContactNameBoundingBox( aItemRect,
                                          iContactNameFont,
                                          aHighlight,
                                          fav,
                                          iContactDataManager->GetContactThumbnailSetting() );
    TRect nameRectUnMirrored = boundingBox; // used for favourite star drawing

    if ( AknLayoutUtils::LayoutMirrored() )
        {
        boundingBox = MirrorLayoutBoundingBox( aItemRect, boundingBox );
        }

    // favourite icon size is set the same as contact name bounding box height.
    TInt favouriteIconSize = boundingBox.Height();
    
    TRect arrowRect = ArrowIconBoundingBox( aItemRect );
    if ( AknLayoutUtils::LayoutMirrored() )
        {
        arrowRect = MirrorLayoutBoundingBox( aItemRect, arrowRect );
        }
    
    // Draw arrow icon if the item is in focus.
    if ( aHighlight )
        {
        DrawArrowIcon( aGc, arrowRect );
        }

    TInt textWidth( 0 );
    TInt err( KErrNone );
    TRAP( err, textWidth = DrawTextWithMatchHighlightL(
            boundingBox, aGc, cellText, iContactNameFont, aColors, aHighlight ) );

    if ( !err && TextUtils::ColumnText( cellText , 2, aText ) == KErrNone ) 
        {
        TRect companyNameBoundingBox = CompanyNameBoundingBox( 
                aItemRect, iCompanyNameFont, aHighlight, iContactDataManager->GetContactThumbnailSetting() );
        if ( AknLayoutUtils::LayoutMirrored() )
            {
            companyNameBoundingBox = MirrorLayoutBoundingBox( aItemRect, companyNameBoundingBox );
            }
        TRAP( err, DrawTextWithMatchHighlightL(
                companyNameBoundingBox, aGc, cellText, iCompanyNameFont, aColors, aHighlight ) );
        }

    if ( !err && fav )
        {
        // Draws the Favourite Icon
        DrawFavouriteIcon( aGc, nameRectUnMirrored, textWidth, aItemRect );
        }
    
    if ( transApi )
        {
        aGc.CancelClippingRect();
        transApi->StopDrawing();
        }
    }

// -----------------------------------------------------------------------------
// ContactThumbnailDrawing
// 
// Draws the Contact Thumbnail Icon if any else draws the dummy contact thumbnail
// -----------------------------------------------------------------------------

TBool CEasyDialingListBoxData::ContactThumbnailDrawing(CWindowGc& aGc, TRect aBoundingBox, TPtrC aCellText) const
    {
    TBool fav(EFalse);
    CFbsBitmap* thumbnail(NULL);
    TBool isLoaded = iContactDataManager->GetThumbnailAndFav(aCellText, thumbnail, fav);
    if ( isLoaded && thumbnail )
        {
        // center the thumbnail in its rect
        TSize size(thumbnail->SizeInPixels());
        TInt xOffset = (aBoundingBox.Width() - size.iWidth) / 2;
        TInt yOffset = (aBoundingBox.Height() - size.iHeight) / 2;
        TPoint tl(aBoundingBox.iTl.iX + xOffset, aBoundingBox.iTl.iY + yOffset);
        TRect sourceRect( TPoint(0,0),size);
        aGc.BitBlt(tl, thumbnail, sourceRect);
        }
    else if ( isLoaded && iContactDataManager->GetContactThumbnailSetting() )
        {
        // draw dummy thumnbnail, but only if we know that the contact doesn't
        // have a thumbnail, and thumbnail drawing is enabled.
        AknIconUtils::SetSize(iDummyThumbnail->Bitmap(), aBoundingBox.Size());
        AknIconUtils::SetSize(iDummyThumbnail->Mask(), aBoundingBox.Size());
        aGc.BitBltMasked( aBoundingBox.iTl, iDummyThumbnail->Bitmap(),
                TRect( TPoint(0,0), aBoundingBox.Size() ), 
                iDummyThumbnail->Mask(), ETrue );
        }
    return fav;
    }


// -----------------------------------------------------------------------------
// DrawArrowIcon
//  Draws the Action Menu 
// -----------------------------------------------------------------------------
void CEasyDialingListBoxData::DrawArrowIcon( 
        CWindowGc& aGc, TRect aArrowRect) const
    {
    // Action Menu Arrow for opening the Action Menu
    if ( AknLayoutUtils::LayoutMirrored() )
        {
        // For mirrored layout the UI logic is mirrored as well
        AknIconUtils::SetSize( iArrowPointingLeft->Mask(), aArrowRect.Size() );
        aGc.BitBltMasked( aArrowRect.iTl, iColorBitmap,
                TRect( TPoint(0,0), aArrowRect.Size() ), 
                iArrowPointingLeft->Mask(), ETrue );

        }
    else
        {
        AknIconUtils::SetSize( iArrowPointingRight->Mask(), aArrowRect.Size() );
        aGc.BitBltMasked( aArrowRect.iTl, iColorBitmap,
                TRect( TPoint(0,0), aArrowRect.Size() ), 
                iArrowPointingRight->Mask(), ETrue );
        }
    }

// -----------------------------------------------------------------------------
// DrawFavouriteIcon
// 
// Draws the Favourite Icon
// -----------------------------------------------------------------------------
void CEasyDialingListBoxData::DrawFavouriteIcon(
        CWindowGc& aGc, 
         TRect aNameRectUnMirrored,
         TInt aTextWidth,
         TRect aEffectiveRect) const
    {
    CFbsBitmap* favouriteIcon;
    CFbsBitmap* favouriteIconMask;
    TRect favouriteIconBoundingBox;

    favouriteIconBoundingBox = FavouriteIconBoundingBox( aNameRectUnMirrored, aTextWidth );

    if ( AknLayoutUtils::LayoutMirrored() )
        {
        favouriteIconBoundingBox = MirrorLayoutBoundingBox(aEffectiveRect, favouriteIconBoundingBox);
        }

    aGc.SetBrushStyle( CGraphicsContext::ENullBrush );
    TRect sourceRect( TPoint(0,0), favouriteIconBoundingBox.Size() );

    if( AknsUtils::SkinInstance()->GetCachedItemData(KAknsIIDQgnFsContactsFavorite) )
        {
        AknsDrawUtils::DrawCachedImage( AknsUtils::SkinInstance(), aGc, sourceRect, 
                KAknsIIDQgnFsContactsFavorite );
        }
    else
        {
        // Create the bitmap and mask to draw.
        TRAP_IGNORE( AknIconUtils::CreateIconL(favouriteIcon, favouriteIconMask, KFavouriteIconBitmapFile, 
                EMbmPhonebook2Qgn_prop_pb_topc, EMbmPhonebook2Qgn_prop_pb_topc_mask ));

        // Set size for the bitmap and mask
        AknIconUtils::SetSize(favouriteIcon, favouriteIconBoundingBox.Size());
        AknIconUtils::SetSize(favouriteIconMask, favouriteIconBoundingBox.Size());
        aGc.BitBltMasked( favouriteIconBoundingBox.iTl , favouriteIcon, sourceRect, favouriteIconMask, ETrue);
        }
    }
// -----------------------------------------------------------------------------
// SetContactDataManager
// 
// -----------------------------------------------------------------------------
// 
void CEasyDialingListBoxData::SetContactDataManager(CEasyDialingContactDataManager* aContactDataManager)
    {
    iContactDataManager = aContactDataManager;
    }

// -----------------------------------------------------------------------------
// HandleItemSizeChange
// 
// -----------------------------------------------------------------------------
//
void CEasyDialingListBoxData::HandleItemSizeChange()
    {
    TInt height = iControl->ItemHeight();
    TSize size( height, height );
    TRAP_IGNORE( UpdateColorBitmapL( size ) );
    
    // Obtain fonts.
    ObtainFonts( height );
    
    // TODO: also bounding boxes could be updated and stored here
    }

// -----------------------------------------------------------------------------
// ObtainFonts
// 
// -----------------------------------------------------------------------------
//       
void CEasyDialingListBoxData::ObtainFonts( TInt aItemHeight )
    {
    CWsScreenDevice& screenDev = *( CEikonEnv::Static()->ScreenDevice() );
    
    // Release previous fonts. ReleaseFont function can cope with null pointers
    // so we don't need to null check them.
    screenDev.ReleaseFont( iContactNameFont );
    screenDev.ReleaseFont( iCompanyNameFont );
    
    // Get a logical font to a basis for our own fonts.
    const CFont* logicalFont = AknLayoutUtils::FontFromId( EAknLogicalFontSecondaryFont );

    // Extract font information
    TFontSpec fontSpec = logicalFont->FontSpecInTwips();
    
    // Calculate contact name font height in TWIPs.
    TInt fontHeightPixels = aItemHeight * KContactNameFontHeightPercent / KCent;
    TInt fontHeightTwips = screenDev.VerticalPixelsToTwips( fontHeightPixels );
    
    // Set height, weight, and posture.
    fontSpec.iHeight = fontHeightTwips;
    fontSpec.iFontStyle.SetStrokeWeight( EStrokeWeightBold );
    fontSpec.iFontStyle.SetPosture( EPostureUpright );
    
    // Obtain contact name font
    TInt err = screenDev.GetNearestFontToDesignHeightInTwips( iContactNameFont, fontSpec );
    __ASSERT_DEBUG( err == KErrNone, EasyDialingPanic( EEasyDialingNoFontFound ) ); 
    
    // Calculate company name font height in TWIPs.
    fontHeightPixels = aItemHeight * KCompanyNameFontHeightPercent / KCent;
    fontHeightTwips = screenDev.VerticalPixelsToTwips( fontHeightPixels );
    fontSpec.iHeight = fontHeightTwips;
    fontSpec.iFontStyle.SetStrokeWeight( EStrokeWeightNormal );
    
    // Obtain company name font.  
    err = screenDev.GetNearestFontToDesignHeightInTwips( iCompanyNameFont, fontSpec );
    __ASSERT_DEBUG( err == KErrNone, EasyDialingPanic( EEasyDialingNoFontFound ) ); 
   }


// -----------------------------------------------------------------------------
// UpdateColorBitmapL
// ColorBitmap is redrawn when UI layout or text color (Theme) changes
// -----------------------------------------------------------------------------
//       
void CEasyDialingListBoxData::UpdateColorBitmapL( const TSize& aSize )
    {
    TRgb color;
    // Get the HighLighted text color in ListBox from the Theme
    AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), color, 
                               KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG10 );
    
    // Create a bitmap with the similar display mode than what the device currently
    // uses for using it as an offscreen bitmap
    AknIconConfig::TPreferredDisplayMode mode;
    AknIconConfig::PreferredDisplayMode( mode, AknIconConfig::EImageTypeOffscreen );
    User::LeaveIfError( iColorBitmap->Create( aSize, mode.iBitmapMode ) );

    // Create a new drawing device and graphics context for enabling drawing to
    // the offscreen bitmap
    CFbsBitmapDevice* destinationDevice = CFbsBitmapDevice::NewL( iColorBitmap );
    CleanupStack::PushL(destinationDevice);

    CFbsBitGc* destinationGc;
    User::LeaveIfError( destinationDevice->CreateContext( destinationGc ) );

    // Set the color and style for pen and brush and draw a rectangle to the
    // bitmap graphics
    destinationGc->SetPenColor( color );
    destinationGc->SetPenStyle( CGraphicsContext::ESolidPen );
    destinationGc->SetBrushColor( color );
    destinationGc->SetBrushStyle( CGraphicsContext::ESolidBrush );
    destinationGc->DrawRect( TRect( TPoint( 0,0 ), aSize ) );

    // Colorbitmap is ready, cleanup
    delete destinationGc;    
    CleanupStack::PopAndDestroy(destinationDevice); 
    }

// -----------------------------------------------------------------------------
// SetEDLBXControl
// 
// -----------------------------------------------------------------------------
//       
void CEasyDialingListBoxData::SetEDLBXControl(CEasyDialingListBox* aControl)
    {
    iControl = aControl;
    }


/*
 * ==============================================================================
 * 
 * Local functions
 * 
 * ==============================================================================
 */


// -----------------------------------------------------------------------------
// ContactImageBoundingBox
// Calculates the area where contact thumbnail is confined
// -----------------------------------------------------------------------------
//
TRect ContactImageBoundingBox(const TRect& aItemRect)
    {
    TInt leftMargin = aItemRect.Width() * KMarginXPercent / KCent;
    TInt topMargin = KMarginYPercent * aItemRect.Height() / KCent;
    TInt bottomMargin = topMargin;
    TInt width = ((aItemRect.Height() - topMargin - bottomMargin) * KThumbnailAspectRatio) / KCent;

    return TRect(
            aItemRect.iTl.iX + leftMargin,
            aItemRect.iTl.iY + topMargin,
            aItemRect.iTl.iX + leftMargin + width,
            aItemRect.iBr.iY - bottomMargin);  
    }

// -----------------------------------------------------------------------------
// ContactNameBoundingBox
// Calculates the area to which the contact name and possible match highlights
// are confined.
//      
// -----------------------------------------------------------------------------
//
static TRect ContactNameBoundingBox(
        const TRect& aItemRect,
        const CFont* aContactNameFont,
        TBool aIsCurrentItem,
        TBool aIsFavourite,
        TBool aThumbnailsShown )
    {
    // Position X will contain the starting position of text from left side of item rect.
    TInt positionX = aItemRect.Width() * KMarginXPercent / KCent;
    
    if (aThumbnailsShown)
        {
        // If contact image is shown, text starts from right side of contact picture + margin.
        TRect contactImageBoundingBox = ContactImageBoundingBox( aItemRect );
        positionX += contactImageBoundingBox.Width();
        positionX += aItemRect.Width() * KMarginXPercent / KCent;
        }
    
    TInt topMargin = KContactNameYOffsetPercent * aItemRect.Height() / KCent;
    TInt height = KTextBoundingBoxHeightPercent * aContactNameFont->FontMaxHeight() / KCent;
    TInt rightMargin = KMarginXPercent * aItemRect.Width() / KCent;
    
    // Reserve space for communication launcher icon.
    // Communication launcher icon is shown only id item is highlighted.
    if ( aIsCurrentItem )
        {
        rightMargin += KArrowIconSizePercent * aItemRect.Height() / KCent;
        }
    
    // If item is favourite, reserve space for favourite icon. Icon dimensions are the same as bounding box height.
    if ( aIsFavourite )
        {
        rightMargin += height;
        }
    
    return TRect(
        aItemRect.iTl.iX + positionX,
        aItemRect.iTl.iY + topMargin,
        aItemRect.iTl.iX + aItemRect.Width() - rightMargin,
        aItemRect.iTl.iY + topMargin + height);
    }


// -----------------------------------------------------------------------------
// CompanyNameBoundingBox
// Calculates the area to which the company name and possible match highlights
// are confined.
// -----------------------------------------------------------------------------
//
static TRect CompanyNameBoundingBox(
        const TRect& aItemRect, 
        const CFont* aCompanyNameFont, 
        TBool aIsCurrentItem,
        TBool aThumbnailsShown )
    {
    // Position X will contain the starting position of text from left side of item rect.
    TInt positionX = aItemRect.Width() * KMarginXPercent / KCent;
    
    if (aThumbnailsShown)
        {
        // If contact image is show, text starts from right side of contact picture + margin.
        TRect contactImageBoundingBox = ContactImageBoundingBox( aItemRect );
        positionX += contactImageBoundingBox.Width();
        positionX += aItemRect.Width() * KMarginXPercent / KCent;
        }
    
    TInt topMargin = KCompanyNameYOffsetPercent * aItemRect.Height() / KCent;
    TInt height = KTextBoundingBoxHeightPercent * aCompanyNameFont->FontMaxHeight() / KCent;
    TInt rightMargin = KMarginXPercent * aItemRect.Width() / KCent;

    // Reserve space for communication launcher icon.
    // Communication launcher icon is shown only id item is highlighted.
    if ( aIsCurrentItem )
        {
        rightMargin += KArrowIconSizePercent * aItemRect.Height() / KCent;
        }
    
    return TRect(
        aItemRect.iTl.iX + positionX,
        aItemRect.iTl.iY + topMargin,
        aItemRect.iTl.iX + aItemRect.Width() - rightMargin,
        aItemRect.iTl.iY + topMargin + height);
    }


// -----------------------------------------------------------------------------
// ArrowIconBoundingBox
// Calculates the area to which the action menu icon is drawn.
// -----------------------------------------------------------------------------
//
TRect ArrowIconBoundingBox(const TRect& aItemRect)
    {
    TInt iconSize = KArrowIconSizePercent * aItemRect.Height() / KCent;
    TInt rightMargin = KMarginXPercent * aItemRect.Width() / KCent;
    TInt positionX = aItemRect.iBr.iX - rightMargin - iconSize;
    TInt topMargin = ( aItemRect.Height() - iconSize ) / 2; // Icon is vertically centered.

    return TRect(
        positionX,
        aItemRect.iTl.iY + topMargin,
        positionX + iconSize,
        aItemRect.iTl.iY + topMargin + iconSize );
    }


// -----------------------------------------------------------------------------
// FavouriteIconBoundingBox
// Calculates the area to which the favourite icon is drawn.
// Because favourite icon is drawn in the same line with contact name,
// this function takes contact name bounding box as the parameter, not
// the whole item rect. 
// -----------------------------------------------------------------------------
//
static TRect FavouriteIconBoundingBox( const TRect& aContactNameBoundingBox, TInt aTextWidth )
    {
    return TRect(
            aContactNameBoundingBox.iTl.iX + aTextWidth,
            aContactNameBoundingBox.iTl.iY,
            aContactNameBoundingBox.iTl.iX + aTextWidth + aContactNameBoundingBox.Height(),
            aContactNameBoundingBox.iBr.iY);
    }



// -----------------------------------------------------------------------------
// BaseLineOffset
//
// -----------------------------------------------------------------------------
//
static TInt BaseLineOffset( const TRect& aTextBoundingBox, const CFont* aFont )
    {
    TInt fontHeight = aFont->FontMaxHeight();
    TInt topMargin = KTextPlacementPercent * (aTextBoundingBox.Height() - fontHeight) / KCent;
    return fontHeight + topMargin - aFont->FontMaxDescent();
    }


// -----------------------------------------------------------------------------
// MirrorLayoutBoundingBox
//
// -----------------------------------------------------------------------------
//
static TRect MirrorLayoutBoundingBox(const TRect& aSourceRect, TRect& aBoundingBoxRect)
    {
    return TRect(
            aSourceRect.iTl.iX + aSourceRect.iBr.iX - aBoundingBoxRect.iBr.iX,
            aBoundingBoxRect.iTl.iY,
            aSourceRect.iTl.iX + aSourceRect.iBr.iX - aBoundingBoxRect.iTl.iX,
            aBoundingBoxRect.iBr.iY);
    }


// -----------------------------------------------------------------------------
// ClipTextToWidth
// Cuts the text in the given pixel width. Also considers match highlight
// issues.
// NOTE: Can change the aMatch parameter if considers that there is too little
// width for matching highlight.
// NOTE 2: This function cannot handle right-to-left or bidirectional text currently.
// These cases must be handled elsewhere.
// -----------------------------------------------------------------------------
//

_LIT( KThreeDots, "..." );

static void ClipTextToWidth(
        TDes& aText,
        const CFont& aFont,
        TInt aMaxWidthInPixels,
        TBool& aMatch)
    {
    TInt minimumWidth = aFont.TextWidthInPixels( KThreeDots );
    
    // If this is a matching piece of text, also match text marginals need to be counted.
    if ( aMatch )
        {
        minimumWidth += 2 * KMatchingTextMarginInPixels;
        }
    
    if ( minimumWidth > aMaxWidthInPixels )
        {
        
        // Not enough space for any text. 
        aText.Zero();
        aMatch = EFalse; // No match highlight shown.
        return;
        }
    
    if ( aMatch )
        {
        aMaxWidthInPixels -= 2 * KMatchingTextMarginInPixels;
        }
    
    AknTextUtils::ClipToFit( aText, aFont, aMaxWidthInPixels );    
    }


// -----------------------------------------------------------------------------
// DrawPieceOfText
// Draws a piece of text of contact, either matching or not matching.
// Updates aXOffset argument by adding drawn text width.
//
// @return: ETrue, if there was enough space for the text to draw. 
// NOTE: CWindowGc font must be set before calling this function. 
// -----------------------------------------------------------------------------
//
static TBool DrawPieceOfText(
        const TRect& aBoundingBox,
        TInt& aXOffset,
        CWindowGc &aGc, 
        const TDesC& aText,
        TBool aMatch,
        const CFont* aFont,
        const CEasyDialingListBoxData::TExtendedColors& aColors,
        TBool aHighLight )
    {    
    if (aText.Length() == 0) 
        {
        return ETrue;
        }
    
    HBufC* newText = aText.Alloc();
    if ( !newText )
        {
        return EFalse;
        }
    
    TPtr textPtr = newText->Des();
    
    // textWidth is the width needed for the text. 
    TInt textWidth = AknBidiTextUtils::MeasureTextBoundsWidth( *aFont, textPtr, CFont::TMeasureTextInput::EFVisualOrder );
    
    // whole width includes also possible highlight margins. 
    TInt wholeWidth = textWidth + ( aMatch ? 2 * KMatchingTextMarginInPixels : 0);
    
    // availableWidth is the space left for drawing. 
    TInt availableWidth = aBoundingBox.Width() - aXOffset;
    
    if ( availableWidth < wholeWidth )
        {
        ClipTextToWidth( textPtr, *aFont, availableWidth, aMatch );
        wholeWidth = availableWidth;
        if ( aMatch )
            {
            textWidth = wholeWidth - (2 * KMatchingTextMarginInPixels);
            }
        else
            {
            textWidth = wholeWidth;
            }
        }
    
    // textBox is rectangle for text without highlight.
    TRect textBox( aBoundingBox );
    textBox.iTl.iX += aXOffset;
    textBox.iBr.iX = textBox.iTl.iX + textWidth;
    
    TInt baseLineOffset = BaseLineOffset( textBox, aFont );
    
    if ( aMatch )
        {
        // highlightBox rectangle for text with highlight.
        // Compared to no highlight, it has extra margins on both sides.
        TRect highlightBox( textBox );
        highlightBox.iBr.iX = highlightBox.iTl.iX + wholeWidth;
        
        // Also boundingBox is shifted right for one margin width.
        textBox.iTl.iX += KMatchingTextMarginInPixels;
        textBox.iBr.iX += KMatchingTextMarginInPixels;
       
        // Outline of matching highlight is of same colour as text.
        // This looks quite OK.
        aGc.SetPenColor( aColors.iMatchingText );
        aGc.SetBrushStyle( CGraphicsContext::ESolidBrush );
        aGc.SetBrushColor( aColors.iMatchingBack );
        
        aGc.DrawRoundRect( highlightBox, TSize( KMatchingTextMarginInPixels, KHighligthRectangleRoundingYInPixels ) );
        
        aGc.SetBrushStyle( CGraphicsContext::ENullBrush );
        }
    else 
        {
        if ( aHighLight )
            {
            aGc.SetPenColor( aColors.iHighlightedText );                 
            }
        else 
            {
            aGc.SetPenColor( aColors.iText );                   
            }
        }
    
    aGc.DrawText( textPtr, textBox, baseLineOffset );
    delete newText;
    aXOffset += wholeWidth;
    return ETrue;
    }


// -----------------------------------------------------------------------------
// DrawTextWithMatchHighlight
// Draws a string with match highlight. Highlighted and non-highlighted
// parts are separated with KHighlightSeparatorChar.
// The first text part is not highlighted and from that on highlight
// is on on every other text piece.
// 
// -----------------------------------------------------------------------------
//
static TInt DrawTextWithMatchHighlightL(
        const TRect& aBoundingBox,
        CWindowGc &aGc, 
        const TDesC& aText,
        const CFont* aFont,
        const CEasyDialingListBoxData::TExtendedColors& aColors,
        TBool aHighLight)
    {
    TInt xOffset = 0;
    
    HBufC* visualBuf = ConvertToVisualAndClipLC( aText, *aFont, aBoundingBox );
    
    TInt calculatedTextWidth = CalculateTextWidth( aBoundingBox, *visualBuf, aFont );
    
    if ( AknLayoutUtils::LayoutMirrored() )
        {
        xOffset = aBoundingBox.Width() - calculatedTextWidth;
        }
    TPtrC textPiece;
    TInt textPieceIndex = 0;
    TBool match = EFalse;

    aGc.UseFont( aFont );
    aGc.SetBrushStyle( CGraphicsContext::ENullBrush );

    while ( TextUtils::ColumnText( textPiece , textPieceIndex, visualBuf, KHighlightSeparatorChar) == KErrNone ) 
        {
        if (! DrawPieceOfText( aBoundingBox, xOffset, aGc, textPiece, match, aFont, aColors, aHighLight ))
            {
            // If there was not enough space for this piece of text, exit the loop stop drawing further pieces.
            break;
            }

        // Toggle match
        match = !match;

        ++textPieceIndex;
        }
    
    CleanupStack::PopAndDestroy( visualBuf );

    aGc.DiscardFont();
    
    return calculatedTextWidth;
    }

// -----------------------------------------------------------------------------
// CalculateTextWidth
// Calculates the width of the text and returns it 
// -----------------------------------------------------------------------------
//
static TInt CalculateTextWidth(const TRect& aBoundingBox, const TDesC& aText, const CFont* aFont )
    {
    
    TInt xOffset = 0;
    TPtrC textPiece;
    TInt textPieceIndex = 0;
    TBool match = EFalse;

    while ( TextUtils::ColumnText( textPiece , textPieceIndex, &aText, KHighlightSeparatorChar) == KErrNone ) 
        {
        if (! CalculateTextPieceWidth( aBoundingBox, xOffset, textPiece, match, aFont ))
            {
            // If there was not enough space for this piece of text, exit the loop stop drawing further pieces.
            break;
            }

        // Toggle match
        match = !match;

        ++textPieceIndex;
        }

    return xOffset;
    }

// -----------------------------------------------------------------------------
// CalculateTextPieceWidth
// 
// Calculates the width of the text piece of highlighted text. 
// The function is aware of the available width for the text, and can take
// possible clippings into account. 
//
// The available width is given by parameters aBoundingBox (space for all text
// pieces) and aXOffset (now much of that space has already been used).
//
// The function adds the text width to the aXOffset reference parameter.
// Returns EFalse, if there is no more space for new text pieces, otherwise
// ETrue.
//
// This function contains the same logic as function DrawPieceOfText.
// -----------------------------------------------------------------------------
//
static TBool CalculateTextPieceWidth(
        const TRect& aBoundingBox,
        TInt& aXOffset,
        const TDesC& aText,
        TBool aMatch,
        const CFont* aFont )
    {    
    if (aText.Length() == 0) 
        {
        return ETrue;
        }

    // textWidth is the width needed for the text. 
    TInt textWidth = AknBidiTextUtils::MeasureTextBoundsWidth( *aFont, aText, CFont::TMeasureTextInput::EFVisualOrder );

    // whole width includes also possible highlight margins. 
    TInt wholeWidth = textWidth + ( aMatch ? 2 * KMatchingTextMarginInPixels : 0);

    // availableWidth is the space left for drawing. 
    TInt availableWidth = aBoundingBox.Width() - aXOffset;

    if ( availableWidth < wholeWidth )
        {

        // We get to this branch, if there is not enough space for the text piece.
        HBufC* newText = aText.Alloc();
        if ( !newText )
            {
            return EFalse;
            }

        TPtr textPtr = newText->Des();
        
        // Clip the text so that it fits the space.
        ClipTextToWidth( textPtr, *aFont, availableWidth, aMatch );

        if ( textPtr.Length() > 0 )
            {
            wholeWidth = AknBidiTextUtils::MeasureTextBoundsWidth( *aFont, textPtr, CFont::TMeasureTextInput::EFVisualOrder );
            wholeWidth += (aMatch ? 2 * KMatchingTextMarginInPixels : 0);

            aXOffset += wholeWidth;
            }

        delete newText;

        return EFalse;
        }

    aXOffset += wholeWidth;

    return ETrue;
    }

// -----------------------------------------------------------------------------
// ContainsRightToLeftText
// 
// Returns true if argument descriptor contains right-to-left text.
// -----------------------------------------------------------------------------
//
static TBool ContainsRightToLeftText( const TDesC& aDesc )
    {
    TBool rtlFound = EFalse;
    
    // TRunInfoArray contains information of the directionalities of the different sections of the aText    
    TBidirectionalState::TRunInfo array[ KMaxRunInfoArrayCount ];
    
    // Initialize the TBidiLogicalToVisual converter for making the conversion from logical to visual order
    TBidiLogicalToVisual converter( aDesc, array, KMaxRunInfoArrayCount );
    
    // Do the reordering. Amount of different directionality sections is returned.
    TInt count( converter.Reorder() );
    // If there are more directionality blocks than we are prepared to handle, just ignore
    // the rest. Those shouldn't fit the screen anyway.
    count = Min( count, KMaxRunInfoArrayCount );
    
    for ( TInt i = 0 ; i < count && !rtlFound ; i++ )
        {
        // iDirection is 0 for left-to-right text.
        if (array[i].iDirection)
            {
            rtlFound = ETrue;
            }
        }

    return rtlFound;
    }

// -----------------------------------------------------------------------------
// ConvertToVisualAndClipL
// 
// Clip bidirectional text to given box and convert it to visual order, ensuring
// that the match highlights don't get broken in the process. Result is given
// as new heap descriptor which is left to CleanupStack.
// NOTE1: No clipping happens here if given descriptor contains only 
// left-to-right text. 
// NOTE2: It's assumed that there can be highlights only in pure LTR and RTL
// texts. Highlights for mixed LTR-RTL text cannot be handled properly.
// -----------------------------------------------------------------------------
//
static HBufC* ConvertToVisualAndClipLC( const TDesC& aText, 
                                       const CFont& aFont, 
                                       const TRect& aBoundingBox )
    {
    HBufC* buf = HBufC::NewLC( aText.Length() + KAknBidiExtraSpacePerLine );
    TPtr ptr = buf->Des();
    ptr.Copy( aText );
    
    // Calling AknBidiTextUtils::ConvertToVisualAndClip doesn't work correctly 
    // with the highlight separator characters (they are not considered to be 
    // zero-length). To minimise the problem, we call the function only when 
    // necessary, i.e. when given text really contains RTL text. This should be 
    // considered as temporary solutions because now the problem of excessive 
    // truncation still remains with RTL languages.
    if ( ContainsRightToLeftText( ptr ) )
        {
        AknBidiTextUtils::ConvertToVisualAndClipL(
                ptr, aFont, aBoundingBox.Width(),
                aBoundingBox.Width() );
        
        // If there's an odd number of highlight separators in the RTL text, 
        // then the matching and and not-matching parts have gone 
        // off-sync in the visual conversion (because parts are drawn starting 
        // from left and first part is always interpreted as not-matching part).
        // Fix this by adding one highlight separator.
        TInt sepCount = HighlightSeparatorCount( ptr );
        if ( sepCount % 2 )
            {
            if ( ptr.Length() == ptr.MaxLength() )
                {
                // There's no more space available. We need to reallocate the 
                // buffer in order to fit the extra separator character.
                HBufC* newBuf = buf->ReAllocL( buf->Length() + 1 );
                CleanupStack::Pop( buf ); // original buf deleted by ReAllocL
                buf = newBuf;
                CleanupStack::PushL( buf );
                ptr.Set( buf->Des() );
                }
            ptr.Insert( 0, KHighlightSeparatorCharAsLit );
            }
        }
    
    return buf;
    }

// -----------------------------------------------------------------------------
// HighlightSeparatorCount
// 
// Returns number of match highlight separator characters in the text
// -----------------------------------------------------------------------------
//
static TInt HighlightSeparatorCount( const TDesC& aText )
    {
    TInt len = aText.Length();
    TInt sepCount = 0;
    
    for ( TInt i = 0; i < len; i++ )
        {
        if ( aText[i] == KHighlightSeparatorChar )
            {
            sepCount++;
            }
        }
    
    return sepCount;
    }

//  End of File.