phoneuis/easydialing/src/easydialinglistboxdata.cpp
branchRCL_3
changeset 9 8871b09be73b
child 14 24062c24fe38
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phoneuis/easydialing/src/easydialinglistboxdata.cpp	Fri Feb 19 22:50:26 2010 +0200
@@ -0,0 +1,1319 @@
+/*
+* 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.
+