meetingrequest/mrgui/mrfieldbuildercommon/src/cesmrrichtextviewer.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 31 Mar 2010 21:08:33 +0300
branchRCL_3
changeset 16 4ce476e64c59
parent 4 e7aa27f58ae1
child 17 8592a65ad3fb
permissions -rw-r--r--
Revision: 201011 Kit: 201013

/*
* Copyright (c) 2009 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 : CEikRichTextEditor based Rich Text viewer
*  Version     : %version: e002sa32#36 %
*
*/

#include "cesmrrichtextviewer.h"

#include "mesmrlistobserver.h"// SCROLLING_MOD: List observer header
#include "esmrconfig.hrh"
#include "esmrhelper.h"
#include "cesmrfieldcommandevent.h"
#include "mesmrfieldeventqueue.h"
#include "cesmrrichtextlink.h"
#include "cesmrcontactmenuhandler.h"
#include "nmrbitmapmanager.h"
#include "nmrcolormanager.h"
#include "esmrfieldbuilderdef.h"
#include "cesmrfield.h"

#include <esmrgui.rsg>
#include <commonphoneparser.h>
#include <finditemengine.h>
#include <txtrich.h>
#include <aknsutils.h>
#include <aknbiditextutils.h>
#include <aknutils.h>
#include <eikenv.h>
#include <data_caging_path_literals.hrh>
#include <baclipb.h> // for clipboard copy
#include <txtclipboard.h>

// DEBUG
#include "emailtrace.h"

// Unnamed namespace for local definitions
namespace{ // codescanner::namespace

#ifdef _DEBUG
    enum TPanic
        {
        EParaFormatNotInitialised = 1
        };
    void Panic( TPanic aPanic )
        {
        _LIT( KCategory, "CESMRRichTextViewer" );
        User::Panic( KCategory(), aPanic );
        }
#endif // DEBUG

// Side margin in twips, needed because not possible to set it in pixels
const TInt KDefaultTextSideMargin( 1 );

}//namespace

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

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::NewL
// -----------------------------------------------------------------------------
//
EXPORT_C CESMRRichTextViewer* CESMRRichTextViewer::NewL(
        const CCoeControl* aParent)
    {
    FUNC_LOG;
    CESMRRichTextViewer* self = new (ELeave) CESMRRichTextViewer;
    CleanupStack::PushL ( self );
    self->ConstructL( aParent );
    CleanupStack::Pop ( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::~CESMRRichTextViewer
// -----------------------------------------------------------------------------
//
EXPORT_C CESMRRichTextViewer::~CESMRRichTextViewer( )
    {
    FUNC_LOG;
    iLinkList.ResetAndDestroy ( );
    iESMRStatic.Close ( );
    delete iParaFormat;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::PositionChanged
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::PositionChanged( )
    {
    FUNC_LOG;
    CTextView* view = TextView ( );
    if ( view )
        {
        view->SetViewRect ( Rect ( ) );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::FocusChanged
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::FocusChanged(
        TDrawNow aDrawNow )
    {
    FUNC_LOG;
    CEikRichTextEditor::FocusChanged( aDrawNow );
    if ( !TextLayout() || !TextView() )
        {
        // not constructed totally:
        return;
        }

    // Check the current selection.
    // If a link has been highlighted, don't change it.
    TCursorSelection selection = Selection();

    // Gaining focus
    if ( IsFocused()
         && iLinkList.Count() > 0
         && selection.Length() == 0 )
        {
        // We need the field indexes, cast required
        CESMRField* parent = static_cast< CESMRField* >( Parent() );

        iCntMenuHdlr->SetContactMenuObserver( this );
        // Focus coming from above ...
        if( parent->CurrentItemIndex() > parent->PreItemIndex() )
            {
            // ... Highlight first link
            TRAP_IGNORE( HighlightLinkL( *( iLinkList[0] ) ) );
            }
        // Focus coming from below ...
        else
            {
            // ... Highlight last link
            TRAP_IGNORE( HighlightLinkL( *( iLinkList[iLinkList.Count() - 1 ] ) ) );
            }
        DrawDeferred();
        }

    // Losing focus
    if ( !IsFocused() )
        {
        TRAP_IGNORE(
                SetSelectionL( CursorPos(), CursorPos() );
                ResetActionMenuL();
                );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::FindTextLinkBetweenNextScrollArea
// -----------------------------------------------------------------------------
//
TInt CESMRRichTextViewer::FindTextLinkBetweenNextScrollArea(
        TInt aStartRow,
        TInt aEndRow,
        TCursorPosition::TMovementType aDirection)
    {
    FUNC_LOG;
    TInt linkRow(KErrNotFound);

    // fetch the line number where the next link is located:
    if ( aDirection == TCursorPosition::EFLineDown )
        {
        for (TInt i = 0; i<iLinkList.Count(); i++)
            {
            linkRow = ValidLinkForFocusing(i, aDirection, aStartRow, aEndRow);
            if ( linkRow != KErrNotFound)
                {
                return linkRow;
                }
            }
        }
    else
        for (TInt i = iLinkList.Count()-1; i>=0 ; i--)
            {
            linkRow = ValidLinkForFocusing(i, aDirection, aStartRow, aEndRow);
            if ( linkRow != KErrNotFound)
                {
                return linkRow;
                }
            }

    return linkRow;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ValidLinkForFocusing
// -----------------------------------------------------------------------------
//
TInt CESMRRichTextViewer::ValidLinkForFocusing(
        TInt aIndex,
        TCursorPosition::TMovementType aDirection,
        TInt aStartRow,
        TInt aEndRow )
    {
    FUNC_LOG;

    if ( iLinkList.Count() > aIndex)
	    {
	    TInt currPos( KErrNotFound );
        const CESMRRichTextLink* currentLink = GetSelectedLink();
        if ( currentLink )
            {
            currPos = currentLink->StartPos();
            }

        TInt pos = iLinkList[ aIndex ]->StartPos();

	    TInt checkRow = TextLayout()->GetLineNumber( pos );

	    if ( checkRow >= aStartRow && checkRow <= aEndRow )
	        {
	        if ( aDirection == TCursorPosition::EFLineDown &&
	             pos > currPos ||
	             aDirection == TCursorPosition::EFLineUp &&
	             ( pos < currPos || currPos == KErrNotFound ) )
	            {
	            // link found between next scroll area.
	            return checkRow;
	            }
	        }
	    }
    return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::OfferKeyEventL
// -----------------------------------------------------------------------------
//
EXPORT_C TKeyResponse CESMRRichTextViewer::OfferKeyEventL(
        const TKeyEvent &aKeyEvent,
        TEventCode aType )
    {
    FUNC_LOG;
    TKeyResponse response( EKeyWasNotConsumed );

    // Handle only event keys.
    if( aType != EEventKey )
        {
        return response;
        }

    /*
     * Handles scrolling between rich text links.
     */
    const CESMRRichTextLink* selectedLink = GetSelectedLink();
    TInt linkCount = iLinkList.Count();

    // If a link is selected, it means that we are operating within
    // the field and can move focus between the links.
    if( linkCount && selectedLink )
        {
        TInt selectedLinkIndex = iLinkList.Find( selectedLink );

        switch ( aKeyEvent.iCode )
            {
            case EKeyLeftArrow:
            case EKeyUpArrow:
                {
                // If possible and exists ...
                if( selectedLinkIndex > 0 &&
                        selectedLinkIndex < linkCount )
                    {
                    // ...Highlight previous link.
                    HighlightLinkL( *( iLinkList[ selectedLinkIndex - 1 ] ) );
                    response = EKeyWasConsumed;

                    // View update required, for example if link is out of
                    // viewable area.
                    UpdateViewL( aKeyEvent );
                    }

                break;
                }
            case EKeyRightArrow:
            case EKeyDownArrow:
                {
                // If possible and exists ...
                if( selectedLinkIndex + 1 < linkCount &&
                        selectedLinkIndex >= 0 )
                    {
                    // ...Highlight next link.
                    HighlightLinkL( *( iLinkList[ selectedLinkIndex + 1 ] ) );
                    response = EKeyWasConsumed;

                    // View update required, for example if link is out of
                    // viewable area.
                    UpdateViewL( aKeyEvent );
                    }

                break;
                }
            case EKeyDevice3: // Selection key
                {
                // No implementation. Non-MSK devices might require this.
                break;
                }
            default:
                break;
            }
        }

    return response;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::HandlePointerEventL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::HandlePointerEventL( const TPointerEvent& aPointerEvent )
    {
    if ( Rect().Contains( aPointerEvent.iPosition) )
        {
        TBool linkFound( EFalse );

        switch ( aPointerEvent.iType )
            {
            case TPointerEvent::EButton1Down:
            case TPointerEvent::EDrag:
                {
                RRegion linkArea;
                CleanupClosePushL( linkArea );

                // Find matching link
                TInt count = iLinkList.Count();

                for ( TInt i = 0; i < count; ++i )
                    {
                    CESMRRichTextLink* link = iLinkList[ i ];
                    GetLinkAreaL( linkArea, *link );

                    if ( linkArea.Contains( aPointerEvent.iPosition ) )
                        {
                        if ( link != GetSelectedLink() )
                            {
                            iCntMenuHdlr->SetContactMenuObserver( this );
                            HighlightLinkL( *link );
                            DrawDeferred();
                            }

                        linkFound = ETrue;
                        break;
                        }
                    }

                CleanupStack::PopAndDestroy( &linkArea );

                break;
                }

            case TPointerEvent::EButton1Up:
                {
                const CESMRRichTextLink* link = GetSelectedLink();

                TBool menuAvailable = iCntMenuHdlr->OptionsMenuAvailable();

                if ( link
                     && ( menuAvailable
                     || link->Type() == CESMRRichTextLink::ETypeLocationUrl
                     || link->Type() == CESMRRichTextLink::ETypeAttachment ) )
                    {
                    linkFound = ETrue;

                    LinkSelectedL();
                    }
                else if ( link && !menuAvailable )
                    {
                    linkFound = ETrue;
                    iOpenActionMenu = ETrue;
                    }
                break;
                }
            default:
                {
                break;
                }
            }

        if ( !linkFound )
            {
            // Tap on plain text
            TextView()->ClearSelectionL();
            ResetActionMenuL();
            DrawDeferred();
            }

        }

    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetMargins
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetMargins( TInt sMargin )
	{
	// Set new value for left and right margins
    TextView()->SetMarginWidths( sMargin ,0);
	}

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetTextL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetTextL(
        const TDesC* aText,
        TBool aSearchLinks )
    {
    FUNC_LOG;

    iLinkList.ResetAndDestroy();

    TextView()->SetMarginWidths( KDefaultTextSideMargin ,0);

    // Clear edwin text
    CEikEdwin::SetCursorPosL( 0, EFalse );
    CEikRichTextEditor::SetTextL( &KNullDesC );
    RichText()->Reset();

    // text lenght plus one to ensure the formatting
    // used is full, not band formatting.
    SetUpperFullFormattingLength( aText->Length() + 1 );

    // Set new edwin text
    CEikEdwin::SetTextLimit( aText->Length ( ) );
    CEikRichTextEditor::SetTextL( aText );

    //Make sure cursor is invisible and selection visible
    TextView()->SetCursorVisibilityL(
            TCursor::EFCursorInvisible,
            TCursor::EFCursorInvisible );
    TextView()->SetSelectionVisibilityL( ETrue );

    // Search text for links (highlights)
    if ( aSearchLinks )
        {
        SearchLinksL( *aText );
        // find first link.
        FindTextLinkBetweenNextScrollArea(0, KMaxAddressFieldLines, TCursorPosition::EFLineDown);
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetLinkObserver
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetLinkObserver(
        MESMRRichTextObserver* aLinkObserver )
    {
    FUNC_LOG;
    iLinkObserver = aLinkObserver;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::AddLinkL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::AddLinkL( CESMRRichTextLink* aLink )
    {
    FUNC_LOG;
    // If this is first link, set it highlighted
    TBool highlight = IsFocused()&& ( iLinkList.Count() == 0 );

    // Reserve space for new link
    iLinkList.ReserveL( iLinkList.Count() + 1 );

    RichText()->ApplyCharFormatL( iRichTextLinkFormat,
                                  iRichTextLinkFormatMask,
                                  aLink->StartPos(),
                                  aLink->Length() );

    // Append aLink to link list. Space has been reserved, so this cannot fail.
    iLinkList.AppendL( aLink );

    if ( highlight )
        {
        HighlightLinkL( *aLink );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::InsertLinkL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::InsertLinkL( CESMRRichTextLink* aLink,
                                                TInt aPosition )
    {
    FUNC_LOG;
    // If this is first link, set it highlighted
    TBool highlight = IsFocused()&& ( iLinkList.Count() == 0 );

    // Reserve space for new link
    iLinkList.ReserveL( iLinkList.Count() + 1 );

    RichText()->ApplyCharFormatL( iRichTextLinkFormat,
                                  iRichTextLinkFormatMask,
                                  aLink->StartPos(),
                                  aLink->Length() );

    // Insert aLink to link list. Space has been reserved, so this cannot fail.
    // InsertL panics if aPosition is illegal.
    iLinkList.InsertL( aLink, aPosition );

    if ( highlight )
        {
        HighlightLinkL( *aLink );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::GetSelectedLink
// -----------------------------------------------------------------------------
//
EXPORT_C const CESMRRichTextLink* CESMRRichTextViewer::GetSelectedLink( ) const
    {
    FUNC_LOG;
    CESMRRichTextLink* link( NULL );

    TCursorSelection currentSelection = Selection();

    for ( TInt i = 0; i < iLinkList.Count(); ++i )
        {
        CESMRRichTextLink* tempLink = iLinkList[i];
        TInt startPos = tempLink->StartPos();
        TInt length = tempLink->Length();

        TCursorSelection linkSelection( startPos, startPos + length );

        if( currentSelection.iCursorPos == linkSelection.iCursorPos &&
                currentSelection.iAnchorPos == linkSelection.iAnchorPos )
            {
            link = tempLink;
            break;
            }
        }

    return link;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::GetLinkTextL
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC* CESMRRichTextViewer::GetLinkTextLC(
        const CESMRRichTextLink& aLink ) const
    {
    FUNC_LOG;
    return RichText()->Read( aLink.StartPos(), aLink.Length() ).AllocLC();
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::CESMRRichTextViewer
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetListObserver(
        MESMRListObserver* aObserver )
    {
    FUNC_LOG;
    iObserver = aObserver;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::RowHeight
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CESMRRichTextViewer::RowHeight()
    {
    FUNC_LOG;
    TInt bandHeight = TextLayout()->BandHeight();
    // Starts from zero, so let's increase the count by one.
    TInt lineNumberCount = TextLayout()->GetLineNumber( TextLength() ) + 1;

    return ( bandHeight / lineNumberCount );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::LineCount
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CESMRRichTextViewer::LineCount()
    {
    FUNC_LOG;

    // First row is 0, so let's add one...
    return ( TextLayout()->GetLineNumber( TextLength() ) + 1 );
    }
// -----------------------------------------------------------------------------
// CESMRRichTextViewer::CurrentLineNumber
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CESMRRichTextViewer::CurrentLineNumber()
    {
    FUNC_LOG;
    // first line is zero, let's increase it by one
    return TextLayout()->GetLineNumber( CursorPos() ) + 1;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetFontL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetFontL( const CFont* aFont )
    {
    FUNC_LOG;
    // iFont is stored for changing font color when losing or
    // gaining focus
    iFont = aFont;
    if ( !iParaFormat )
        {
        iParaFormat = new ( ELeave ) CParaFormat();
        }

    // All this required, otherwise the font loses its
    // antialiasing drawing.
    iParaFormat->iLineSpacingControl = CParaFormat::ELineSpacingExactlyInPixels;

    iParaFormatMask.SetAttrib( EAttLineSpacing );
    iParaFormat->iHorizontalAlignment = CParaFormat::ELeftAlign;
    iParaFormat->iVerticalAlignment = CParaFormat::ECenterAlign;
    iParaFormatMask.SetAttrib( EAttAlignment );
    iParaFormatMask.SetAttrib( EAttVerticalAlignment );

    iCharFormat.iFontSpec = iFont->FontSpecInTwips();

    iCharFormatMask.SetAttrib( EAttFontTypeface );
    iCharFormatMask.SetAttrib( EAttFontHeight );
    iCharFormatMask.SetAttrib( EAttFontPosture );
    iCharFormatMask.SetAttrib( EAttFontStrokeWeight );

    iCharFormat.iFontPresentation.iTextColor =
        NMRColorManager::Color( NMRColorManager::EMRMainAreaTextColor );

    iCharFormatMask.SetAttrib( EAttColor );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetLineSpacingL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetLineSpacingL( TInt aLineSpacingInTwips )
    {
    __ASSERT_DEBUG( iParaFormat, Panic( EParaFormatNotInitialised ) );
    iParaFormatMask.SetAttrib( EAttLineSpacing );
    iParaFormatMask.SetAttrib( EAttLineSpacingControl );
    iParaFormat->iLineSpacingControl =
        CParaFormat::ELineSpacingExactlyInPixels;

    iParaFormat->iLineSpacingInTwips = aLineSpacingInTwips;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ApplyLayoutChanges
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::ApplyLayoutChangesL()
    {
    __ASSERT_DEBUG( iParaFormat, Panic( EParaFormatNotInitialised ) );
    CRichText* richtext = RichText();
    TInt paraCount( richtext->ParagraphCount() );
    // Go through each paragraph and apply the same format
    for( TInt i = 0; i < paraCount; ++i  )
        {
        TInt paraLen( 0 );   // Length of paragraph
        TInt fiChPos( 0 );   // First character position of paragraph
        // Get the length of the paragraph
        fiChPos = richtext->CharPosOfParagraph( paraLen, i );
        richtext->ApplyParaFormatL(
                iParaFormat, iParaFormatMask, fiChPos, paraLen );
        richtext->ApplyCharFormatL(
                iCharFormat, iCharFormatMask, fiChPos, paraLen );
        }
    // This forces CEikEdwin::OnReformatL to notify observer
    // through HandleEdwinSizeEventL about changed size.
    // It is notified only if linecount changes.
    CEikEdwin::iNumberOfLines = 0;
    CEikEdwin::FormatTextL();
    }


// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetSelectedLink
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetFocusLink( TInt aLinkIndex )
    {
    if ( ( aLinkIndex >= 0 ) && ( aLinkIndex < iLinkList.Count() ) )
        {
        TRAP_IGNORE( HighlightLinkL( *( iLinkList[aLinkIndex] ) ) );
        DrawDeferred();
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::GetSelectedLink
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CESMRRichTextViewer::GetFocusLink( ) const
    {
    FUNC_LOG;
    // "-1" stand for no link be selected now.
    TInt linkIndex( KErrNotFound );
    if ( 0 == iLinkList.Count() )
        {
        return linkIndex;
        }
    
    TCursorSelection currentSelection = Selection();

    for ( TInt i = 0; i < iLinkList.Count(); ++i )
        {
        CESMRRichTextLink* tempLink = iLinkList[i];
        TInt startPos = tempLink->StartPos();
        TInt length = tempLink->Length();

        TCursorSelection linkSelection( startPos, startPos + length );

        if( currentSelection.iCursorPos == linkSelection.iCursorPos &&
                currentSelection.iAnchorPos == linkSelection.iAnchorPos )
            {
            linkIndex = i;
            break;
            }
        }

    return linkIndex;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::CESMRRichTextViewer
// -----------------------------------------------------------------------------
//
CESMRRichTextViewer::CESMRRichTextViewer( )
    {
    FUNC_LOG;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ConstructL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ConstructL(const CCoeControl* aParent)
    {
    FUNC_LOG;

    TInt flags = CEikEdwin::EReadOnly |
                 CEikEdwin::EResizable |
                 CEikEdwin::ENoAutoSelection |
                 CEikEdwin::EAvkonDisableCursor;
    if( !aParent )
        {
        flags |= CEikEdwin::EOwnsWindow;
        }

    CEikRichTextEditor::ConstructL( aParent, 1, 1, flags );
    SetSuppressBackgroundDrawing( ETrue );

    iESMRStatic.ConnectL();
    iCntMenuHdlr = &iESMRStatic.ContactMenuHandlerL();
    iRichTextLinkFormatMask.SetAttrib( EAttFontUnderline );
    iRichTextLinkFormat.iFontPresentation.iUnderline = EUnderlineOn;
    iRichTextLinkFormatMask.SetAttrib( EAttColor );
    iRichTextLinkFormat.iFontPresentation.iTextColor =
        NMRColorManager::Color(
                NMRColorManager::EMRCutCopyPasteHighlightColor );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::HighlightLinkL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::HighlightLinkL(const CESMRRichTextLink& aLink )
    {
    FUNC_LOG;
    TCursorSelection selection( aLink.StartPos(), aLink.StartPos() + aLink.Length() );

    // If TextView is not constructed yet.
    if ( !TextView() )
        {
        SetSelectionL( selection.iCursorPos, selection.iAnchorPos );
        }
    else
        {
        TextView()->SetPendingSelection( selection );
        }

    SetValueL( aLink );
    ChangeMiddleSoftkeyL( aLink );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetValueL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::SetValueL( const CESMRRichTextLink& aLink )
    {
    FUNC_LOG;
    // Instantiate contact action menu for selected link
    switch ( aLink.Type() )
        {
        case CESMRRichTextLink::ETypeEmail:
            {
            iCntMenuHdlr->SetValueL( aLink.Value(),
            		CESMRContactMenuHandler::EValueTypeEmail );
            break;
            }
        case CESMRRichTextLink::ETypePhoneNumber:
            {
            iCntMenuHdlr->SetValueL( aLink.Value(),
            		CESMRContactMenuHandler::EValueTypePhoneNumber );
            break;
            }
        case CESMRRichTextLink::ETypeURL:
            {
            iCntMenuHdlr->SetValueL( aLink.Value(),
            		CESMRContactMenuHandler::EValueTypeURL );
            break;
            }
        case CESMRRichTextLink::ETypeAttachment:
            {

            break;
            }
        default:
            {
            break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SearchLinksL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::SearchLinksL(const TDesC& aText )
    {
    FUNC_LOG;
    // Search for emails, phone numbers and urls
    TInt searchCases = CFindItemEngine::EFindItemSearchMailAddressBin |
                       CFindItemEngine::EFindItemSearchPhoneNumberBin |
                       CFindItemEngine::EFindItemSearchURLBin;

    CFindItemEngine* itemEngine =
              CFindItemEngine::NewL ( aText,
                       ( CFindItemEngine::TFindItemSearchCase ) searchCases );
    CleanupStack::PushL (itemEngine );

    const CArrayFixFlat<CFindItemEngine::SFoundItem>
            * foundItems = itemEngine->ItemArray ( );

    // For each found item
    for (TInt i=0; i<foundItems->Count ( ); ++i )
        {
        // iItemType, iStartPos, iLength
        const CFindItemEngine::SFoundItem& item = foundItems->At (i );
        HBufC* valueBuf = aText.Mid (item.iStartPos, item.iLength ).AllocL ( );
        CleanupStack::PushL (valueBuf );

        CESMRRichTextLink::TType type = (CESMRRichTextLink::TType) -1;
        switch (item.iItemType )
            {
            case CFindItemEngine::EFindItemSearchMailAddressBin:
                {
                type = CESMRRichTextLink::ETypeEmail;
                break;
                }
            case CFindItemEngine::EFindItemSearchPhoneNumberBin:
                {
                type = CESMRRichTextLink::ETypePhoneNumber;

                // Remove unsupported characters from phone number
                TPtr phonePtr = valueBuf->Des ( );
                CommonPhoneParser::ParsePhoneNumber (phonePtr,
                        CommonPhoneParser::EContactCardNumber );
                break;
                }
            case CFindItemEngine::EFindItemSearchURLBin:
                {
                type = CESMRRichTextLink::ETypeURL;
                break;
                }
            default:
                {
                break;
                }
            }

        if (valueBuf->Length ( )> 0 && type >= 0 )
            {
            CESMRRichTextLink* link = CESMRRichTextLink::NewL (item.iStartPos,
                    item.iLength, *valueBuf, type,
                    CESMRRichTextLink::ETriggerKeyRight );
            CleanupStack::PushL( link );
            AddLinkL( link );
            CleanupStack::Pop( link );
            }

        CleanupStack::PopAndDestroy (valueBuf );
        }

    // T
    HandleTextChangedL();

    CleanupStack::PopAndDestroy (itemEngine );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::CopyCurrentLinkToClipBoardL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::CopyCurrentLinkToClipBoardL() const
    {
    FUNC_LOG;
    const CESMRRichTextLink* link = GetSelectedLink();
    HBufC* clipBoardText = NULL;

    if ( link )
    	{
    	clipBoardText = GetLinkTextLC( *link );
    	}

    if ( clipBoardText )
        {
        CClipboard* cb =
        CClipboard::NewForWritingLC( CCoeEnv::Static()->FsSession() );
        cb->StreamDictionary().At( KClipboardUidTypePlainText );
        CPlainText* plainText = CPlainText::NewL();
        CleanupStack::PushL( plainText );
        plainText->InsertL( 0 , *clipBoardText);
        plainText->CopyToStoreL( cb->Store(),
        		cb->StreamDictionary(), 0, plainText->DocumentLength());
        CleanupStack::PopAndDestroy( plainText );
        cb->CommitL();
        CleanupStack::PopAndDestroy( cb );
        CleanupStack::PopAndDestroy( clipBoardText );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ResetActionMenu
// -----------------------------------------------------------------------------
EXPORT_C void CESMRRichTextViewer::ResetActionMenuL() const // codescanner::LFunctionCantLeave
    {
    FUNC_LOG;
    if ( iCntMenuHdlr )
        {
        iCntMenuHdlr->Reset();
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetEventObserver
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetEventQueue(
        MESMRFieldEventQueue* aEventQueue )
    {
    FUNC_LOG;
    iEventQueue = aEventQueue;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::LinkSelectedL
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CESMRRichTextViewer::LinkSelectedL()
    {
    FUNC_LOG;
    TBool linkSelected = EFalse;

    const CESMRRichTextLink* link = GetSelectedLink();
    if ( link )
        {
        switch ( link->TriggerKey() )
            {
            case CESMRRichTextLink::ETriggerKeyRight:
                {
                if ( !iLinkObserver ||
                     !iLinkObserver->HandleRichTextLinkSelection(link) )
                    {
                    ShowContextMenuL();
                    }
                linkSelected = ETrue;
                break;
                }
            case CESMRRichTextLink::ETriggerKeyOk:
                {
                if (iLinkObserver &&
                    iLinkObserver->HandleRichTextLinkSelection( link ) )
                    {
                    linkSelected = ETrue;
                    }
                break;
                }
            default:
                {
                break;
                }
            }
        }

    return linkSelected;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ProcessCommandL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ProcessCommandL( TInt aCommandId )
    {
    FUNC_LOG;
    if ( iEventQueue )
        {
        // Forward commands from contact menu handler as command events.
        // Contact menu handler has the command observer for binary
        // compatibility reason.
        CESMRFieldCommandEvent* event =
            CESMRFieldCommandEvent::NewLC( NULL, aCommandId );
        iEventQueue->NotifyEventL( *event );
        CleanupStack::PopAndDestroy( event );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ActivateL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ActivateL()
    {
    FUNC_LOG;
    CEikRichTextEditor::ActivateL();

    // CEikEdwin::ActivateL removes selection, re-set highlight
    // if focused and there's a selected link.
    const CESMRRichTextLink* link = GetSelectedLink();
    if ( link && IsFocused() )
        {
        HighlightLinkL( *link );
        DrawDeferred();
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ContactActionQueryComplete
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ContactActionQueryComplete()
    {
    FUNC_LOG;
    if ( iOpenActionMenu )
        {
        // Activate link as actions have been discovered
        TRAP_IGNORE(  LinkSelectedL() );
        }

    // Reset menu observer
    iCntMenuHdlr->SetContactMenuObserver( NULL );
    iOpenActionMenu = EFalse;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ShowContextMenuL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ShowContextMenuL()
    {
    FUNC_LOG;
    iOpenActionMenu = EFalse;
    ProcessCommandL( EAknSoftkeyContextOptions );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::GetLinkAreaL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::GetLinkAreaL(
        TRegion& aRegion,
        const CESMRRichTextLink& aLink ) const
    {
    FUNC_LOG;
    TTmDocPosSpec posSpec;
    TTmPosInfo2 posInfo;

    posSpec.iPos = aLink.StartPos();
    posSpec.iType = TTmDocPosSpec::ELeading;

    aRegion.Clear();

    if ( TextView()->FindDocPosL( posSpec, posInfo ) )
        {
        TRect linkRect( 0, 0, 0, 0 );

        // Create link surrounding rectangle
        HBufC* text = GetLinkTextLC( aLink );
        TInt textWidth = AknBidiTextUtils::MeasureTextBoundsWidth(
                *iFont,
                *text,
                CFont::TMeasureTextInput::EFVisualOrder );

        TPoint tl( posInfo.iEdge );

        if ( AknLayoutUtils::LayoutMirrored() )
            {
            // move top left x to end of text
            tl.iX -= textWidth;
            }

        tl.iY -= iFont->FontMaxAscent();
        TPoint br( tl.iX + textWidth, tl.iY + iFont->FontMaxHeight() );

        TRect rect( Rect() );

        while ( ( tl.iX < rect.iTl.iX || br.iX > rect.iBr.iX )
                && !aRegion.CheckError() )
            {
            // Link on multiple lines

            tl.iX = Max( rect.iTl.iX, tl.iX );
            br.iX = Min( br.iX, rect.iBr.iX );
            linkRect.SetRect( tl, br );
            aRegion.AddRect( linkRect );

            TPtrC remainder( text->Mid( posSpec.iPos - aLink.StartPos() ) );
            TInt numChars = iFont->TextCount( remainder,
                                              linkRect.Width() );
            posSpec.iPos += numChars;
            remainder.Set( remainder.Mid( numChars) );

            if ( TextView()->FindDocPosL( posSpec, posInfo ) )
                {
                textWidth = AknBidiTextUtils::MeasureTextBoundsWidth(
                                *iFont,
                                remainder,
                                CFont::TMeasureTextInput::EFVisualOrder );

                tl = posInfo.iEdge;

                if ( AknLayoutUtils::LayoutMirrored() )
                    {
                    // move top left x to end of text
                    tl.iX -= textWidth;
                    }

                tl.iY -= iFont->FontMaxAscent();
                br.SetXY( tl.iX + textWidth, tl.iY + iFont->FontMaxHeight() );
                }
            }

        linkRect.SetRect( tl, br );
        aRegion.AddRect( linkRect );

        CleanupStack::PopAndDestroy( text );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ChangeMiddleSoftkeyL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ChangeMiddleSoftkeyL( const CESMRRichTextLink& aLink )
    {
    FUNC_LOG;
    CESMRField* field = static_cast< CESMRField* >( Parent() );

    if ( field )
        {
        field->ChangeMiddleSoftKeyL( aLink.MSKCommand(), aLink.MSKText() );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::UpdateViewL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::UpdateViewL( const TKeyEvent &aKeyEvent )
    {
    FUNC_LOG;
    const CESMRRichTextLink* selectedLink = GetSelectedLink();

    if( !selectedLink )
        {
        return;
        }

    RRegion linkRegion;
    CleanupClosePushL( linkRegion );

    GetLinkAreaL( linkRegion, *selectedLink );
    TRect linkRect = linkRegion.BoundingRect();

    CleanupStack::PopAndDestroy( &linkRegion );

    TRect viewableAreaRect = iObserver->ViewableAreaRect();


    switch ( aKeyEvent.iCode )
        {
        case EKeyLeftArrow:
        case EKeyUpArrow:
            {
            if( linkRect.iTl.iY < viewableAreaRect.iTl.iY )
                {
                // Move fields down
                iObserver->RePositionFields(
                        -( linkRect.iTl.iY - viewableAreaRect.iTl.iY ) );
                }

            break;
            }
        case EKeyRightArrow:
        case EKeyDownArrow:
            {
            if( linkRect.iBr.iY > viewableAreaRect.iBr.iY )
                {
                // Move fields up
                iObserver->RePositionFields(
                        -( linkRect.iBr.iY - viewableAreaRect.iBr.iY ) );
                }
            break;
            }
        default:
            break;
        }
    }

// EOF