meetingrequest/mrgui/mrfieldbuildercommon/src/cesmrrichtextviewer.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:39:21 +0200
changeset 0 8466d47a6819
child 4 e7aa27f58ae1
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2003-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
*
*/

#include "cesmrrichtextviewer.h"

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

#include <esmrgui.rsg>
#include <commonphoneparser.h>
#include <finditemengine.h>
#include <txtrich.h>
#include <AknsUtils.h>
#include <eikenv.h>
#include <data_caging_path_literals.hrh>
#include <baclipb.h> // for clipboard copy
#include <aknlongtapdetector.h>
#include <touchfeedback.h>

#ifndef FF_CMAIL_INTEGRATION
#include <txtclipboard.h>
#endif // FF_CMAIL_INTEGRATION

// DEBUG
#include "emailtrace.h"

// <cmail> Removed profiling. </cmail>

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

const TInt KArrowUpperMargin (2);
const TInt KArrowRightMargin (5);

}//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 ( );
    delete iActionMenuIcon;
    delete iActionMenuIconMask;
    iESMRStatic.Close ( );
    delete iLongTapDetector;
    }

// -----------------------------------------------------------------------------
// 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;
        }

    if ( IsFocused() )
        {
        if( iLayout )
            {
            TRAP_IGNORE( SetFontColorL( ETrue ) );
            }

        if ( iCurrentLinkIndex == KErrNotFound )
            {
            TInt linkRow = KErrNotFound;
            if ( CursorPos() == 0 )
                {
                // focus is coming from field above:
                linkRow = FindTextLinkBetweenNextScrollArea(
                		0, 1, TCursorPosition::EFLineDown );
                }
            else
                {
                linkRow = FindTextLinkBetweenNextScrollArea(
                		LineCount()-2,
                        LineCount()-1,
                        TCursorPosition::EFLineUp);
                }
            if ( linkRow != KErrNotFound )
                {
                const CESMRRichTextLink* link = GetSelectedLink ( );
                if (link )
                    {
                    HighlightLink (*link );
                    DrawDeferred ( );
                    }
                }
            }
        else
            {
            const CESMRRichTextLink* link = GetSelectedLink ( );
            HighlightLink( *link );
            DrawDeferred();
            }
        }

    if (!IsFocused())
        {
        // losing focus
        // <cmail> codescanner
		TRAP_IGNORE(SetSelectionL(CursorPos(), CursorPos()));
        if( iLayout )
            {
            TRAP_IGNORE(SetFontColorL( EFalse ));
            }
        // </cmail>
        TRAP_IGNORE( TextView()->SetDocPosL( 0 ));
        }
    }

// -----------------------------------------------------------------------------
// 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;
                }
            }

    iCurrentLinkIndex = KErrNotFound;
    return linkRow;
    }

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


    if(iLinkList.Count() > aIndex)
	    {
        TInt pos = iLinkList[aIndex]->StartPos();

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

	    if ( checkRow >= aStartRow && checkRow <= ( aEndRow ) )
	        {
	        if ( aDirection == TCursorPosition::EFLineDown &&
	             aIndex > iCurrentLinkIndex  ||
	             aDirection == TCursorPosition::EFLineUp &&
	             ( aIndex < iCurrentLinkIndex ||
	               iCurrentLinkIndex == KErrNotFound ))
	            {
	            iCurrentLinkIndex = aIndex;

	            // link found between next scroll area.
	            return checkRow;
	            }
	        }
	    }
    return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetHighLightToNextLinkL
// -----------------------------------------------------------------------------
//
TBool CESMRRichTextViewer::SetHighLightToNextLinkL(
        TCursorPosition::TMovementType aDirection,
        TInt aStartRow,
        TInt aEndRow )
    {
    FUNC_LOG;
    TBool ret( EFalse );

    TInt currentLineNumber = TextLayout()->GetLineNumber( CursorPos() );

    // check is there a link before next scroll point:
    TInt linkRow = FindTextLinkBetweenNextScrollArea(
						aStartRow , aEndRow, aDirection);

    if ( linkRow != KErrNotFound && iCurrentLinkIndex != KErrNotFound )
        {
        const CESMRRichTextLink* link = GetSelectedLink ( );
        if (link )
            {
            HighlightLink (*link );

            if ( iObserver && linkRow != currentLineNumber )
                {
                TInt endOfLink = link->StartPos()+link->Length();
                TInt line = TextLayout()->GetLineNumber( endOfLink );
                if ( aDirection == TCursorPosition::EFLineUp ) // moving up:
                    {
                    iObserver->MoveListAreaDownL(
                            RowHeight() * ( currentLineNumber - line ));
                    }
                else
                    {
                    iObserver->MoveListAreaUpL(
                            RowHeight() * ( line - currentLineNumber ));
                    }
                }

            DrawDeferred ( );
            ret = ETrue;
            }
        }
    else
    	{
    	// no link are focused, do reset work to refresh the option menu.
    	ResetActionMenuL();
    	}

    return ret;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::OfferKeyEventL
// -----------------------------------------------------------------------------
//
EXPORT_C TKeyResponse CESMRRichTextViewer::OfferKeyEventL(
        const TKeyEvent &aKeyEvent,
        TEventCode aType )
    {
    FUNC_LOG;
    // Handle only event keys
    if ( aType == EEventKey )
        {
        if ( iObserver ) // only description field has observer set.
            {
            // fetch the current line number:
            TInt currentLineNumber = TextLayout()->GetLineNumber( CursorPos() );

            if ( aKeyEvent.iCode == EKeyDownArrow && aType == EEventKey )
                {
                if ( !iObserver->IsFieldBottomVisible() &&
                     SetHighLightToNextLinkL(
                    		 TCursorPosition::EFLineDown,
                             currentLineNumber,
                             currentLineNumber + KMaxAddressFieldLines) )
                    {
                    return EKeyWasConsumed;
                    }
                else if ( iObserver->IsFieldBottomVisible() )
                    {
                    if ( SetHighLightToNextLinkL(TCursorPosition::EFLineDown,
                                            currentLineNumber,
                                            iNumberOfLines) )
                        {
                        return EKeyWasConsumed;
                        }
                    // means that the whole control is visible:
                    // and we can skip to next field
                    return EKeyWasNotConsumed;
                    }

                if ( currentLineNumber == iNumberOfLines )
                    {
                    // the end of text has been reached
                    return EKeyWasConsumed;
                    }

                // move three lines...
                ScrollViewL( KMaxAddressFieldLines,
							 TCursorPosition::EFLineDown);


                SetSelectionL( CursorPos(), CursorPos() );
                iCurrentLinkIndex = KErrNotFound;
                if ( iObserver )
                    {
                    iObserver->MoveListAreaUpL(
                    		RowHeight() * KMaxAddressFieldLines );
                    }

                return EKeyWasConsumed;
                }
            else if ( aKeyEvent.iCode == EKeyUpArrow && aType == EEventKey )
                {
                if (iPosition.iY < 0 &&
                    SetHighLightToNextLinkL(
                    		TCursorPosition::EFLineUp,
                            currentLineNumber - KMaxAddressFieldLines,
                            currentLineNumber))
                    {
                    return EKeyWasConsumed;
                    }
                if ( iPosition.iY > 0 )
                    {
                    // before changing the focus the field above,
                    // check is there any links in rest of text
                    if (SetHighLightToNextLinkL(TCursorPosition::EFLineUp,
                                        0,
                                        currentLineNumber))
                        {
                        return EKeyWasConsumed;
                        }

                    // means that the whole control is visible:
                    // and we can skip to next field
                    return EKeyWasNotConsumed;
                    }
                else
                    {
                    TInt currentLineNumber =
                    TextLayout()->GetLineNumber( CursorPos() );
                    // move three lines...
                    ScrollViewL( KMaxAddressFieldLines,
                    		     TCursorPosition::EFLineUp);

                    SetSelectionL( CursorPos(), CursorPos());
                    iCurrentLinkIndex = KErrNotFound;

                     if ( iObserver )
                        {
                        iObserver->MoveListAreaDownL(
                        		RowHeight() * KMaxAddressFieldLines );
                        }

                    currentLineNumber =
						TextLayout()->GetLineNumber( CursorPos() );
                    return EKeyWasConsumed;
                    }
                }
            }
        if ( aKeyEvent.iCode == EKeyRightArrow ||
        	 aKeyEvent.iCode == EKeyDevice3 ||
             aKeyEvent.iCode == EKeyDevice4 ||
             aKeyEvent.iCode == EKeyEnter )
            {
            // Show right click menu (action menu)
            const CESMRRichTextLink* link = GetSelectedLink();
            if (link &&
            	link->TriggerKey ( )== CESMRRichTextLink::ETriggerKeyRight )
                {
                if ( !iLinkObserver ||
                	 !iLinkObserver->HandleRichTextLinkSelection(link) )
                    {
                    iCntMenuHdlr->ShowActionMenuL();
                    }
                return EKeyWasConsumed;
                }
            }
        if ( aKeyEvent.iCode == EKeyLeftArrow )
            {
            const CESMRRichTextLink* link = GetSelectedLink();
            if ( link )
                {
                return EKeyWasConsumed;
                }
            }
        if ( aKeyEvent.iCode == EKeyDevice3 ||
        		aKeyEvent.iCode == EKeyDevice4 ||
        		aKeyEvent.iCode == EKeyEnter )
            {
            // Select link
            const CESMRRichTextLink* link = GetSelectedLink ( );
            if (link &&
            	link->TriggerKey ( )== CESMRRichTextLink::ETriggerKeyOk )
                {
                if (iLinkObserver &&
                	iLinkObserver->HandleRichTextLinkSelection (link ) )
                    {
                    return EKeyWasConsumed;
                    }
                }
            }
        }

    return EKeyWasNotConsumed;
    }


// -----------------------------------------------------------------------------
// CESMRRichTextViewer::ScrollViewL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::ScrollViewL(
        TInt aNumberOfRows,
        TCursorPosition::TMovementType aDirection)
    {
    FUNC_LOG;
    for( TInt i=0; i < aNumberOfRows; i++ )
        {
        MoveCursorL( aDirection, EFalse);
        }
    }

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

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetFontL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetFontL( const CFont* aFont,
                                             CESMRLayoutManager* aLayout )
    {
    FUNC_LOG;
    // These pointers are stored to able font color changing when losing or
    // gaining focus
    iLayout = aLayout;
    iFont = aFont;

    SetFontColorL( IsFocused() );

    // 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::SetFontColor
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::SetFontColorL( TBool aFocused )
    {
    FUNC_LOG;
    // all this stuff is needed to be set, otherwise the
    // font loses its antialiasing drawing

    TFontSpec fontSpec = iFont->FontSpecInTwips();

    CParaFormat paraFormat;
    TParaFormatMask paraFormatMask;
    paraFormat.iLineSpacingControl = CParaFormat::ELineSpacingExactlyInPixels;

    paraFormatMask.SetAttrib( EAttLineSpacing );
    paraFormat.iHorizontalAlignment = CParaFormat::ELeftAlign;
    paraFormatMask.SetAttrib( EAttAlignment );

    TCharFormat charFormat;
    TCharFormatMask formatMask;
    charFormat.iFontSpec = fontSpec;

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

    if( aFocused )
        {
        charFormat.iFontPresentation.iTextColor =
        iLayout->ViewerListAreaHighlightedTextColor();
        }
    else
        {
        charFormat.iFontPresentation.iTextColor = KRgbBlack;
        }
    formatMask.SetAttrib( EAttColor );

    CParaFormatLayer* paraFormatLayer =
        CParaFormatLayer::NewL( &paraFormat, paraFormatMask );
    CleanupStack::PushL( paraFormatLayer );

    CCharFormatLayer* charFormatLayer =
        CCharFormatLayer::NewL( charFormat, formatMask );

    SetParaFormatLayer( paraFormatLayer );
    SetCharFormatLayer( charFormatLayer );

    CleanupStack::Pop( paraFormatLayer );
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::SetTextL
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetTextL(
        const TDesC* aText,
        TBool aSearchLinks )
    {
    FUNC_LOG;
    iCurrentLinkIndex = KErrNotFound;
    iLinkList.ResetAndDestroy ( );

    // Clear edwin text
    CEikEdwin::SetCursorPosL( 0, EFalse );

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

    // Set new edwin text
    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);
        }

    // first row is 0, so let's add one...
    iNumberOfLines = TextLayout()->GetLineNumber( TextLength() ) + 1;
    }

// -----------------------------------------------------------------------------
// 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( iFormat,
                                  iFormatMask,
                                  aLink->StartPos(),
                                  aLink->Length() );

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

    if ( highlight )
        {
        HighlightLink( *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( iFormat,
                                  iFormatMask,
                                  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 )
        {
        HighlightLink( *aLink );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::GetSelectedLink
// -----------------------------------------------------------------------------
//
EXPORT_C const CESMRRichTextLink* CESMRRichTextViewer::GetSelectedLink( ) const
    {
    FUNC_LOG;
    if (iCurrentLinkIndex >= 0 && iCurrentLinkIndex < iLinkList.Count ( ) )
        {
        return iLinkList[iCurrentLinkIndex];
        }
    else
        {
        return NULL;
        }
    }

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

// -----------------------------------------------------------------------------
// 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;
    return iNumberOfLines;
    }
// -----------------------------------------------------------------------------
// 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::CESMRRichTextViewer
// -----------------------------------------------------------------------------
//
EXPORT_C void CESMRRichTextViewer::SetActionMenuStatus( TBool aStatus )
    {
    FUNC_LOG;
    iActionMenuStatus = aStatus;
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::CESMRRichTextViewer
// -----------------------------------------------------------------------------
//
CESMRRichTextViewer::CESMRRichTextViewer( )
:   iActionMenuStatus( ETrue ), iActionMenuOpen( EFalse )
    {
    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 );

    User::LeaveIfError(
            NMRBitmapManager::GetSkinBasedBitmap(
                    NMRBitmapManager::EMRBitmapRightClickArrow, iActionMenuIcon,
                    iActionMenuIconMask, KIconSize ) );

    iESMRStatic.ConnectL ( );
    iCntMenuHdlr = &iESMRStatic.ContactMenuHandlerL();
    iCurrentLinkIndex = KErrNotFound;
    iFormatMask.SetAttrib( EAttFontUnderline );
    iFormat.iFontPresentation.iUnderline = EUnderlineOn;
    iFormatMask.SetAttrib( EAttColor );
    iFormat.iFontPresentation.iTextColor = KRgbBlue;

    iLongTapDetector = CAknLongTapDetector::NewL(this);
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::Draw
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::Draw( const TRect& aRect ) const
    {
    FUNC_LOG;
    CEikEdwin::Draw( aRect );
    if (IsFocused ( ) && iActionMenuStatus )
        {
        const CESMRRichTextLink* link = GetSelectedLink ( );
        if (link && link->TriggerKey ( )== CESMRRichTextLink::ETriggerKeyRight )
            {
            TRAP_IGNORE(DrawRightClickIconL(*link));
            }
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::DrawRightClickIconL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::DrawRightClickIconL(
		const CESMRRichTextLink& aLink ) const
    {
    FUNC_LOG;
    TTmDocPosSpec posSpec;
    TTmPosInfo2 posInfo;

    posSpec.iPos = aLink.StartPos ( )+ aLink.Length ( );
    posSpec.iType = TTmDocPosSpec::ETrailing;

    if (TextView()->FindDocPosL (posSpec, posInfo ) )
        {
        TPoint pt = posInfo.iEdge;
        pt -= iActionMenuIcon->SizeInPixels ( );

        pt.iY = pt.iY + KArrowUpperMargin;
        pt.iX = Parent()->Rect().iBr.iX -
                iActionMenuIcon->SizeInPixels().iWidth - KArrowRightMargin;
        TRect dst(pt, iActionMenuIcon->SizeInPixels ( ));
        TRect src(TPoint (0, 0 ), iActionMenuIcon->SizeInPixels ( ));

        CWindowGc& gc = SystemGc ( );
        gc.DrawBitmapMasked (dst, iActionMenuIcon, src, iActionMenuIconMask,
                EFalse );
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::HighlightLink
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::HighlightLink(const CESMRRichTextLink& aLink )
    {
    FUNC_LOG;
    TInt error = KErrNone;
    TCursorSelection selection( aLink.StartPos(), aLink.StartPos() + aLink.Length() );

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

    if ( error == KErrNone )
        {
        TRAP( error, SetValueL( aLink ) );
        if ( error != KErrNone )
            {
            CEikonEnv::Static()->// codescanner::eikonenvstatic
                HandleError( error );
            }
        }
    }

// -----------------------------------------------------------------------------
// 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:
            {
            // Set this as command observer.
            // Commands from contact menu handler are
            // processed in ProcessCommandL and forwarded
            // as field command events
            // <cmail>
            //iCntMenuHdlr->SetCommandObserver( this );
            // </cmail>
            iCntMenuHdlr->SetValueL( aLink.Value(),
            		CESMRContactMenuHandler::EValueTypeAttachment );
            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();

    if( link )
        {
    	HBufC* clipBoardText = GetLinkTextL(*link);

    	if ( clipBoardText )
    	    {
    	    CleanupStack::PushL( 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::CopyCurrentLinkValueToClipBoardL
// -----------------------------------------------------------------------------
EXPORT_C void CESMRRichTextViewer::CopyCurrentLinkValueToClipBoardL() const
    {
    FUNC_LOG;
    const CESMRRichTextLink* link = GetSelectedLink();

    if( link )
        {
    	TDesC& clipBoardText = link->Value();

	    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 );
        }
    }


// -----------------------------------------------------------------------------
// 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 )
        {
        iActionMenuOpen = ETrue;
        switch ( link->TriggerKey() )
            {
            case CESMRRichTextLink::ETriggerKeyRight:
                {
                if ( !iLinkObserver ||
                     !iLinkObserver->HandleRichTextLinkSelection(link) )
                    {
                    iCntMenuHdlr->ShowActionMenuL();
                    }
                linkSelected = ETrue;
                break;
                }
            case CESMRRichTextLink::ETriggerKeyOk:
                {
                if (iLinkObserver &&
                    iLinkObserver->HandleRichTextLinkSelection (link ) )
                    {
                    linkSelected = ETrue;
                    }
                break;
                }
            default:
                {
                break;
                }
            }
        iActionMenuOpen = EFalse;
        }

    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();

    // Make sure correct font color is in use.
    SetFontColorL( IsFocused() );

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

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::HandlePointerEventL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::HandlePointerEventL(const TPointerEvent& aPointerEvent)
    {
    TBool linkTapped( EFalse );
    CESMRRichTextLink* link = NULL;
    // fetch the current line number:
    TInt currentLineNumber = TextLayout()->GetLineNumber( CursorPos() );
    // find out tapped position
    TPoint touchPoint = aPointerEvent.iPosition;

    TInt tappedPos = TextView()->XyPosToDocPosL( touchPoint );
    for (TInt i = 0; i<iLinkList.Count(); i++)
        {
        link = iLinkList[ i ];
        TInt linkStart = link->StartPos();
        TInt linkEnd = link->StartPos() + link->Length() - 1;
        if ( tappedPos >= linkStart && tappedPos <= linkEnd )
            {
            // link tapped
            linkTapped = ETrue;
            iCurrentLinkIndex = i;
            break;
            }
        }

    if ( aPointerEvent.iType == TPointerEvent::EButton1Down )
        {
        if ( linkTapped )
            {
            // tactile feedback
            MTouchFeedback* feedback = MTouchFeedback::Instance();
            if ( feedback )
                {
                feedback->InstantFeedback( this, ETouchFeedbackBasic );
                }

            TPointerEvent pointerEvent = aPointerEvent;
            pointerEvent.iParentPosition = pointerEvent.iPosition
                + DrawableWindow()->AbsPosition();
            iLongTapDetector->PointerEventL( pointerEvent );
            HighlightLink( *link );
            if ( Parent() )
                {
                Parent()->DrawDeferred();
                }
            else
                {
                DrawDeferred();
                }
            }
        }
    else
        {
        TPointerEvent pointerEvent = aPointerEvent;
        pointerEvent.iParentPosition = pointerEvent.iPosition
            + DrawableWindow()->AbsPosition();
        iLongTapDetector->PointerEventL( pointerEvent );

        if ( aPointerEvent.iType  == TPointerEvent::EButton1Up )
            {
            if ( linkTapped && !iActionMenuOpen )
                {
                // tapped highlighted field, execute action
                LinkSelectedL();
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// CESMRRichTextViewer::HandleLongTapEventL
// -----------------------------------------------------------------------------
//
void CESMRRichTextViewer::HandleLongTapEventL( const TPoint& /*aPenEventLocation*/,
                                              const TPoint& /*aPenEventScreenLocation*/ )
    {
    if ( !iActionMenuOpen )
        {
        LinkSelectedL();
        }
    }
// EOF