/*
* 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: This file implements classes CESMRNcsAifEntry, CESMRNcsAifEditor.
*
*/

#include "emailtrace.h"
#include "cesmrncsaifeditor.h"

#include <AknsDrawUtils.h>
#include <s32mem.h>
#include <txtrich.h>
#include <baclipb.h>
#include <PtiDefs.h>
#include <StringLoader.h>
#include <eikedwin.h>
#include <txtclipboard.h>

#include "cesmrncsemailaddressobject.h"
#include "cesmrcontacthandler.h"
#include "esmrfieldbuilderdef.h"

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

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::NewL
// ---------------------------------------------------------------------------
//
CESMRNcsAifEntry* CESMRNcsAifEntry::NewL(
    const CESMRNcsEmailAddressObject& addr,
    TBool aDisplayFull )
    {
    FUNC_LOG;
    CESMRNcsAifEntry* self = new (ELeave) CESMRNcsAifEntry( aDisplayFull );
    CleanupStack::PushL(self);
    self->ConstructL( addr );
    CleanupStack::Pop(self);
    return self;
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::NewL
// ---------------------------------------------------------------------------
//
CESMRNcsAifEntry* CESMRNcsAifEntry::NewL(
    const TDesC& aDn,
    const TDesC& aEml,
    TBool aDisplayFull )
    {
    FUNC_LOG;
    CESMRNcsAifEntry* self = new ( ELeave ) CESMRNcsAifEntry( aDisplayFull );
    CleanupStack::PushL(self);
    self->ConstructL( aDn, aEml);
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::~CESMRNcsAifEntry
// ---------------------------------------------------------------------------
//
CESMRNcsAifEntry::~CESMRNcsAifEntry()
    {
    FUNC_LOG;
    delete iAddress;
    delete iDisplayString;
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::CESMRNcsAifEntry
// ---------------------------------------------------------------------------
//
CESMRNcsAifEntry::CESMRNcsAifEntry( TBool aDisplayFull ) :
    iDisplayFull( aDisplayFull )
    {
    FUNC_LOG;
    //do nothing
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::ConstructL
// ---------------------------------------------------------------------------
//
void CESMRNcsAifEntry::ConstructL( const TDesC& aDn, const TDesC& aEml )
    {
    FUNC_LOG;
    iAddress = CESMRNcsEmailAddressObject::NewL( aDn, aEml );
    ConstructL();
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::ConstructL
// ---------------------------------------------------------------------------
//
void CESMRNcsAifEntry::ConstructL( const CESMRNcsEmailAddressObject& aAddress )
    {
    FUNC_LOG;
    iAddress = CESMRNcsEmailAddressObject::NewL( aAddress );
    ConstructL();
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::ConstructL
// ---------------------------------------------------------------------------
//
void CESMRNcsAifEntry::ConstructL()
    {
    FUNC_LOG;
    SetDisplayStringL();
    }

// ---------------------------------------------------------------------------
// CESMRNcsAifEntry::SetDisplayStringL
// ---------------------------------------------------------------------------
//
void CESMRNcsAifEntry::SetDisplayStringL()
    {
    FUNC_LOG;
    if ( iDisplayString )
        {
        delete iDisplayString;
        iDisplayString = NULL;
        }

    const TDesC& dname = iAddress->DisplayName();
    const TDesC& email = iAddress->EmailAddress();

    TInt dnameLength = dname.Length();
    TInt emailLength = email.Length();

    TInt length;
    if ( !iIsDup && !iDisplayFull )
        {
        length = dnameLength > 0 ? dnameLength : emailLength;
        length += 1; // ';'

        iDisplayString = HBufC::NewL( length );
        TPtr ptr = iDisplayString->Des();

        ptr.Append( dname.Length() > 0 ? dname : email );
        ptr.Append( KAddressDelimeterSemiColon );
        }
    else
        {
        // Display, Name <display.name(at)dn.com>;
        length = dnameLength + emailLength + 4;

        iDisplayString = HBufC::NewL( length );
        TPtr ptr = iDisplayString->Des();

        ptr.Append( dname );
        _LIT(KLeft, " <");
        ptr.Append( KLeft );
        ptr.Append( email );
        _LIT(KRight, ">");
        ptr.Append( KRight );
        ptr.Append( KAddressDelimeterSemiColon );
        }
    }

// ---------------------------------------------------------------------------
// constructor/destructor
// ---------------------------------------------------------------------------
//
CESMRNcsAifEditor::CESMRNcsAifEditor( CESMRContactHandler& aContactHandler, HBufC* aDefaultText ) :
    CESMRNcsEditor( aDefaultText ), iContactHandler( aContactHandler )
    {
    FUNC_LOG;
    SetEdwinObserver( this );
    }

// ---------------------------------------------------------------------------
// constructor/destructor
// ---------------------------------------------------------------------------
//
CESMRNcsAifEditor::~CESMRNcsAifEditor()
    {
    FUNC_LOG;
    iArray.ResetAndDestroy();
    iAddressArray.Reset();
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::CursorLineNumber() const
// -----------------------------------------------------------------------------
//
TInt CESMRNcsAifEditor::CursorLineNumber() const
    {
    FUNC_LOG;

    TInt ret = iLayout->GetLineNumber( CursorPos() );
    ret++;
    if( ret > KMaxAddressFieldLines )
        {
        ret = KMaxAddressFieldLines;
        }
    return ret;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::LineCount() const
// -----------------------------------------------------------------------------
//
TInt CESMRNcsAifEditor::LineCount() const
    {
    FUNC_LOG;
    TInt lineCount = iLayout->GetLineNumber( TextLength() );
    lineCount++;
    if( lineCount > KMaxAddressFieldLines )
        {
        lineCount = KMaxAddressFieldLines;
        }
    return lineCount;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::OfferKeyEventL()
// -----------------------------------------------------------------------------
//
TKeyResponse CESMRNcsAifEditor::OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType )
    {
    FUNC_LOG;
    TKeyResponse ret = EKeyWasNotConsumed;

    // check if we are copying
    if ( ret == EKeyWasNotConsumed )
        {
        ret = CopyEntriesToClipboardL( aKeyEvent, aType );
        }

    // Check if we need to delete a contact
    // We must do this before select sincey they
    // key off of the same key code.
    if ( ret == EKeyWasNotConsumed )
        {
        ret = HandleContactDeletionL( aKeyEvent, aType );
        }

    // Check if we need to highlight a contact
    if ( ret == EKeyWasNotConsumed )
        {
        ret = SetEditorSelectionL( aKeyEvent, aType );
        }

    if ( ret == EKeyWasNotConsumed )
        {
        ret = CESMRNcsEditor::OfferKeyEventL( aKeyEvent, aType );
        }
    return ret;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::HandleEdwinEventL()
// This function gets called if a character is entered through the FEP.
// Otherwise the character entry is added through OfferKeyEvent
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::HandleEdwinEventL( CEikEdwin* /*aEdwin*/, TEdwinEvent aEventType )
    {
    FUNC_LOG;
    if ( aEventType == MEikEdwinObserver::EEventTextUpdate )
        {
        HandleContactDeletionL();
        HandleTextUpdateL();
        if ( IsVisible() )
        	{
        	DrawDeferred();
        	}
        }
    else if ( aEventType == MEikEdwinObserver::EEventNavigation )
        {
        HandleNavigationEventL();
        }
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::SetEditorSelectionL()
// -----------------------------------------------------------------------------
//
TKeyResponse CESMRNcsAifEditor::SetEditorSelectionL( const TKeyEvent& aKeyEvent, TEventCode aType )
    {
    FUNC_LOG;
    TKeyResponse response = EKeyWasNotConsumed;
    CESMRNcsAifEntry* entry = NULL;
    TCursorSelection selection = Selection();

    // Moving to a new line is a special case.
    // We need to offer the key to the editor control first so it can
    // move the cursor for us.  Then we check if it's in an entry.
    if ( aKeyEvent.iCode == EKeyUpArrow || aKeyEvent.iCode == EKeyDownArrow )
        {
        response = CESMRNcsEditor::OfferKeyEventL( aKeyEvent,aType );
        if ( response == EKeyWasConsumed )
            {
            // We're moving to a new line.
            entry = GetEntryAt( CursorPos() );
            if ( entry )
                {
                SetSelectionL( entry->iCursorPos, entry->iAnchorPos );
                }
            }
        }
    // Check if the cursor is in any of the addresses
    else if( aKeyEvent.iCode == EKeyLeftArrow || aKeyEvent.iCode == EKeyBackspace )
        {
        // We're moving left, but haven't yet.
        entry = GetEntryAt( CursorPos(), EDirectionLeft );
        if ( entry )
            {
            if ( selection.Length() && aKeyEvent.iCode == EKeyLeftArrow)
                {
                // Adds or removes the entry from the current selection.
                SetSelectionL( entry->LowerPos(), selection.iAnchorPos );
                response = EKeyWasConsumed;
                }
            else if ( !selection.Length() )
                {
                SetSelectionL( entry->LowerPos(), entry->HigherPos() );
                response = EKeyWasConsumed;
                }
            }
        }
    else if( aKeyEvent.iCode == EKeyRightArrow || aKeyEvent.iCode == EKeyDelete )
        {
        // We're moving right, but haven't yet.
        entry = GetEntryAt( CursorPos(), EDirectionRight );
        if ( entry )
            {
            if ( selection.Length() && aKeyEvent.iCode == EKeyRightArrow  )
                {
                // Adds or removes the entry form the current selection.
                SetSelectionL( entry->HigherPos(), selection.iAnchorPos );
                response = EKeyWasConsumed;
                }
            else if ( !selection.Length() )
                {
                SetSelectionL( entry->HigherPos(), entry->LowerPos() );
                response = EKeyWasConsumed;
                }
            }
        }

    // Close the address popup if we handled the event
    if ( response == EKeyWasConsumed )
        {
        iAddressPopupList->ClosePopupContactListL();
        }

    DrawDeferred();
    return response;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::HandleContactDeletionL()
// -----------------------------------------------------------------------------
//
TKeyResponse CESMRNcsAifEditor::HandleContactDeletionL(
        const TKeyEvent& aKeyEvent,
        TEventCode aType)
    {
    FUNC_LOG;
    TKeyResponse response  = EKeyWasNotConsumed;
    if ( SelectionLength() && aType == EEventKey
        && !IsNavigationKey( aKeyEvent ) )
        {
        // Delete highlighted entries.
        TCursorSelection selection = Selection();
        TBool entryDeleted = EFalse;
        for ( TInt ii = iArray.Count() - 1; ii >= 0; --ii )
            {
            if ( iArray[ii]->LowerPos() >= selection.LowerPos() &&
                iArray[ii]->HigherPos() <= selection.HigherPos() )
                {
                delete iArray[ii];
                iArray.Remove( ii );
                entryDeleted = ETrue;
                }
            }

        if ( entryDeleted )
            {
            // Check that duplicate entries are correctly marked.
            UpdateDuplicateEntryMarkings();

            // Set the cursor after the entry before the ones we just deleted
            CESMRNcsAifEntry* entry = NULL;
            for ( TInt ii = iArray.Count() - 1; ii >= 0; --ii )
                {
                if ( iArray[ii]->HigherPos() <= selection.LowerPos() )
                    {
                    entry = iArray[ii];
                    break;
                    }
                }

            ClearSelectionL();

            RepositionEntriesL( entry );

            // The key event is set consumed only on delete and backpace
            // events, other events need to be forwarded to the editor.
            if ( aKeyEvent.iCode == EKeyDelete ||
                 aKeyEvent.iCode == EKeyBackspace )
                {
                response = EKeyWasConsumed;
                }
            }
        }
    return response;
    }

// ---------------------------------------------------------
// CESMRNcsAifEditor::DoCharChangeL
// ---------------------------------------------------------
//
void CESMRNcsAifEditor::DoCharChangeL()
    {
    FUNC_LOG;
    RecalculateEntryPositions();

    TChar previousChar = CharAtPos( CursorPos() - 1 );
    TBool sentinel = ( previousChar == KSemiColon ||
        previousChar == KComma );
    if ( sentinel )
        {
        // if comma was pressed we replace it with semicolon
        if ( previousChar == KComma )
            {
            CPlainText* text = Text();
            text->DeleteL( CursorPos() - 1, 1 );
            text->InsertL( CursorPos() - 1, KSemiColon );
            }
        TBool check = ParseNewAddressL();
        }
    UpdateAddressAutoCompletionL();
    }

// ---------------------------------------------------------
// CESMRNcsAifEditor::CharAtPos
// ---------------------------------------------------------
//
TChar CESMRNcsAifEditor::CharAtPos( TInt aPos ) const
    {
    FUNC_LOG;
    if ( aPos >= 0 && aPos < TextLength() )
        {
        TBuf<1> buf;
        Text()->Extract( buf, aPos, 1 );
        return buf[0];
        }
    else
        {
        return 0;
        }
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::SetAddressesL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::SetAddressesL( const RPointerArray<CESMRNcsEmailAddressObject>& aAddresses )
    {
    FUNC_LOG;
    iArray.ResetAndDestroy();
    AppendAddressesL( aAddresses );
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::AppendAddressesL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::AppendAddressesL( const RPointerArray<CESMRNcsEmailAddressObject>& aAddresses )
    {
    FUNC_LOG;
    iArray.ReserveL( iArray.Count() + aAddresses.Count() );

    CESMRNcsAifEntry* entry = NULL;
    for (int i=0;i<aAddresses.Count();i++)
        {
        CESMRNcsEmailAddressObject* address = aAddresses[i];
        entry = CESMRNcsAifEntry::NewL( *address, address->DisplayFull() );

        int idx;
        // Check for duplicate display names
        for ( idx=0; idx<iArray.Count(); idx++ )
            {
            if ( iArray[idx]->IsSameDN( *entry ) )
                {
                iArray[idx]->SetDup();
                entry->SetDup();
                }
            }

        CleanupStack::PushL( entry );
        iArray.AppendL( entry );
        CleanupStack::Pop( entry );
        }

    RepositionEntriesL( entry );
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::GetAddressesL()
// -----------------------------------------------------------------------------
//
const RPointerArray<CESMRNcsEmailAddressObject>& CESMRNcsAifEditor::GetAddressesL()
    {
    // Clear the existing array since it may be out of sync
    iAddressArray.Reset();

    for (int i=0;i<iArray.Count();i++)
        {
        iAddressArray.AppendL(&iArray[i]->Address());
        }

    return iAddressArray;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::GetEntryAt()
// -----------------------------------------------------------------------------
//
CESMRNcsAifEntry* CESMRNcsAifEditor::GetEntryAt(
    TInt aPos,
    TEntryDirection aDirection ) const
    {
    FUNC_LOG;

    TChar KSpace( ' ' );

    for( TInt i = 0; i < iArray.Count(); i++ )
      {
      CESMRNcsAifEntry* entry = iArray[i];
      if ( aDirection == EDirectionNone )
          {
          // no direction, check if cursor is on entry
          if ( entry->Includes( aPos ) )
              {
              return entry;
              }
          }
      else if ( aDirection == EDirectionRight )
          {
          // direction to the righ. check if cursor is on entry or
          // entry is immediately to the right of the cursor
          if ( entry->Includes( aPos ) )
              {
              return entry;
              }

          if ( entry->Start() >= aPos && entry->Start() - aPos <= 1 &&
              CharAtPos( aPos ) == KSpace )
              {
              return entry;
              }
          }
      else if ( aDirection == EDirectionLeft )
          {
          // direction to the left. decrease cursor by one and check if it
          // is on entry or if entry is immediately to the left of the cursor
          if ( entry->Includes( aPos - 1 ) )
              {
              return entry;
              }

          if ( aPos >= entry->End() && aPos - entry->End() <= 1 &&
              CharAtPos( aPos - 1 ) == KSpace )
              {
              return entry;
              }
          }
      }
    return NULL;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::CheckAddressWhenFocusLostL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::CheckAddressWhenFocusLostL()
    {
    FUNC_LOG;
    ParseNewAddressL();
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::ParseNewAddressL()
// -----------------------------------------------------------------------------
//
TBool CESMRNcsAifEditor::ParseNewAddressL()
    {
    FUNC_LOG;
    HBufC* text = GetNonEntryTextLC();

    if ( !text )
        {
        return false;
        }
    // check if there is a name for the email address

    // if name = NULL, it isn't in cleanupstack
    HBufC* name = iContactHandler.GetLastSearchNameLC( *text );
    if ( name )
        {
        AddAddressL( *name, *text, ETrue );
        CleanupStack::PopAndDestroy( name );
        }
    else
        {
        AddAddressL(KNullDesC(),*text);
        }

    CleanupStack::PopAndDestroy(text);

    return true;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::GetNonEntryTextL()
// This will extract any text that was entered that is not
// part of any existing entries
// -----------------------------------------------------------------------------
//
HBufC* CESMRNcsAifEditor::GetNonEntryTextLC() const
    {
    FUNC_LOG;
    // The user entered a new email address directly into the AIF
    TInt pos = CursorPos();

    // Check if the cursor is in an existing entry
    for (int i=0;i<iArray.Count(); i++)
        {
        if (iArray[i]->Includes(pos-1)) return NULL;
        }

    // Pull the text to the left of the cursor up to the
    // previous entry or the beginning of the doc
    int idx = -1;
    for (int i=0;i<iArray.Count();i++)
        {
        if (iArray[i]->End() < pos) idx = i;
        }

    // No entry before this text, pull to beginning of doc
    int len, start;
    if (idx == -1)
        {
        len = pos;
        start = 0;
        }
    else
        {
        len = pos - iArray[idx]->End();
        start = iArray[idx]->End();
        }

    // The user could have moved the cursor into the new text.
    // Pull the text to the right of the cursor up to the
    // next entry or the end of the doc
    for (idx=0;idx<iArray.Count();idx++)
        {
        if (iArray[idx]->Start() >= pos) break;
        }

    // No entry after this text, pull to end of doc
    if (iArray.Count() == 0 || idx == iArray.Count())
        {
        len += TextLength() - pos;
        }
    else
        {
        len += iArray[idx]->Start() - pos;
        }

    // Extract the address text
    HBufC* text = HBufC::NewLC(len);
    TPtr ptr = text->Des();
    Text()->Extract(ptr,start,len);

    // Wipe out any delimiters
    TChar semiColon(KSemiColon);
    pos = ptr.Locate(semiColon);
    if (pos != KErrNotFound)
        ptr.Delete(pos,1);

    // Trim the string and add it to the entries list
    ptr.Trim();

    if (ptr.Length() == 0)
        {
        CleanupStack::PopAndDestroy(text);
        return NULL;
        }

    return text;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::CopyEntriesToClipBoardL
// -----------------------------------------------------------------------------
//
TKeyResponse CESMRNcsAifEditor::CopyEntriesToClipboardL(
    const TKeyEvent& aKeyEvent,
    TEventCode aType )
    {
    FUNC_LOG;
    TKeyResponse ret = EKeyWasNotConsumed;

    const TInt KCopyKeyEventCode = 3;
    const TInt KCutKeyEventCode = 24;

    // check that we are copying
    TBool copyKeyEvent = ( aType == EEventKey && aKeyEvent.iCode == KCopyKeyEventCode &&
                           aKeyEvent.iModifiers & EModifierCtrl &&
                           aKeyEvent.iScanCode == EPtiKeyQwertyC );
    TBool cutKeyEvent = ( aType == EEventKey && aKeyEvent.iCode == KCutKeyEventCode &&
                          aKeyEvent.iModifiers & EModifierCtrl &&
                          aKeyEvent.iScanCode == EPtiKeyQwertyX );
    if ( copyKeyEvent || cutKeyEvent )
        {
        RPointerArray<CESMRNcsAifEntry> entries;
        CleanupClosePushL( entries );
        FindSelectedEntriesL( entries );
        if ( entries.Count() > 0 )
            {
            CancelFepTransaction();
            HBufC* formattedText = GetFormattedAddressListLC( entries, EFalse );
            CopyToClipboardL( *formattedText );
            CleanupStack::PopAndDestroy( formattedText );

            if ( !cutKeyEvent )
                { // cutting needs more handling
                ret = EKeyWasConsumed;
                }
            }
        CleanupStack::PopAndDestroy( &entries );
        }
    return ret;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::FindSelectedEntriesL( )
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::FindSelectedEntriesL( RPointerArray<CESMRNcsAifEntry>& aEntries )
    {
    FUNC_LOG;
    TCursorSelection selection = Selection();
    TInt count = iArray.Count();
    for ( TInt i = 0; i < iArray.Count(); i++ )
        {
        CESMRNcsAifEntry* entry = iArray[i];
        if ( entry->Start() >= selection.LowerPos() &&
             entry->End() <= selection.HigherPos() )
            {
            aEntries.AppendL( entry );
            }
        }
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::EmailAddressIndexNameBySelection( )
// -----------------------------------------------------------------------------
//
const CESMRNcsEmailAddressObject* CESMRNcsAifEditor::EmailAddressObjectBySelection() const

    {
    FUNC_LOG;
    // Find the contact the cursor is in
    const CESMRNcsAifEntry* aEntry = GetEntryAt(CursorPos());
    ASSERT(aEntry != NULL);
    return &aEntry->Address();
    }


// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::AddAddressL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::AddAddressL( const CESMRNcsEmailAddressObject& aAddress )
    {
    FUNC_LOG;
    AddAddressL( CESMRNcsAifEntry::NewL( aAddress, aAddress.DisplayFull() ) );
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::AddAddressL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::AddAddressL(
    const TDesC& aDisplayName,
    const TDesC& aEmail,
    TBool aDisplayFull )
    {
    FUNC_LOG;
    AddAddressL( CESMRNcsAifEntry::NewL( aDisplayName, aEmail, aDisplayFull ) );
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::AddAddressL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::AddAddressL( CESMRNcsAifEntry* aNewEntry )
    {
    FUNC_LOG;
    int idx;
    // Check for duplicate display names
    for (idx=0;idx<iArray.Count();idx++)
        {
        if (iArray[idx]->IsSameDN(*aNewEntry))
            {
            iArray[idx]->SetDup();
            aNewEntry->SetDup();
            }
        }

    // Find the location where we need to insert the address
    TInt pos = CursorPos();
    for (idx=0;idx<iArray.Count();idx++)
        {
        if (pos <= iArray[idx]->Start()) break;
        }
    if (idx == iArray.Count())
        {
        // Tack the address onto the end of the array
        iArray.Append(aNewEntry);
        }
    else
        {
        iArray.Insert(aNewEntry,idx);
        }
    RepositionEntriesL( aNewEntry );
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::RecalculateEntryPositions()
// The text has changed, so recalculate the positions of the items.
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::RecalculateEntryPositions()
    {
    FUNC_LOG;

    TInt pos(0);
    TInt idx(0);
    TInt error(0);
    for	(;idx<iArray.Count();idx++)
    	{
    	    TRAP( error, pos = FindTextL( &iArray[idx]->DisplayString(), pos,
    	        CEikEdwin::EFindCaseSensitive | CEikEdwin::ENoBusyMessage ) );
    	    ASSERT( KErrNone == error && KErrNotFound != pos );
    	    if(pos != iArray[idx]->Start())
    	    	{
    	    	break;
    	    	}
    	    pos += iArray[idx]->Length();
    	}

    if(idx != iArray.Count())
    	{
		for (; idx<iArray.Count(); idx++ )
			{
			pos = iArray[idx]->SetPos( pos );
			pos++; // for whitespace
			}
    	}
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::RepositionEntriesL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::RepositionEntriesL( const CESMRNcsAifEntry* aPosEntry )
    {
    FUNC_LOG;
    TInt pos = 0;
    CESMRNcsAifEntry* aEntry;
    for (int i=0;i<iArray.Count();i++)
        {
        aEntry = iArray[i];
        pos = aEntry->SetPos( pos );
        pos++; // for whitespace
        }

    // Reset the text
    SetCursorPosL( 0, EFalse ); //In case the cursor pos is invalid
    HBufC* text = NULL;
    text = GetFormattedAddressListL( iArray );
    CleanupStack::PushL( text );

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

    // The stupid control will reset all atributes if the text is blank ( font color, etc.).
    if ( text->Length() == 0 )
        {
        CleanupStack::PopAndDestroy( text );
        text = KEmptySpace().AllocL();
        CleanupStack::PushL( text );
        SetTextL( text );
        }
    else
        {
        SetTextL( text );
        }

    CleanupStack::PopAndDestroy( text );
    HandleTextChangedL();

    // Set the cursor at the end of the given entry
    if ( !aPosEntry )
        {
        SetCursorPosL( 0, EFalse );
        }
    else
        {
        SetCursorPosL( aPosEntry->End(), EFalse );
        }
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::GetLookupTextLC()
// -----------------------------------------------------------------------------
//
HBufC* CESMRNcsAifEditor::GetLookupTextLC() const
    {
    FUNC_LOG;
    HBufC* text = GetTextInHBufL();

    if (text == NULL) return NULL;

    CleanupStack::PushL( text );
    TPtr ptr( text->Des() );
    ptr = ptr.LeftTPtr( CursorPos() );
    TChar semiColon(KSemiColon);
    TInt location = ptr.LocateReverse( semiColon );
    if( location != KErrNotFound )
        {
        ptr = ptr.RightTPtr( ptr.Length() - location -1 );
        ptr.TrimLeft();
        }
    return text;
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::GetFormattedAddressListLC()
// -----------------------------------------------------------------------------
//
HBufC* CESMRNcsAifEditor::GetFormattedAddressListLC(
    RPointerArray<CESMRNcsAifEntry>& aEntries,
    TBool aDisplayList ) const
    {
    FUNC_LOG;
    TInt length = CalculateAddressListLength( aEntries, aDisplayList );
    if( length <= 0 )
        {
        return HBufC::NewLC(0);
        }

    HBufC* buf = HBufC::NewLC( length );
    TPtr ptr = buf->Des();

    TInt count = aEntries.Count();
    for( TInt i = 0; i < count; i++ )
        {
        if ( aDisplayList )
            {
            ptr.Append( aEntries[i]->DisplayString() );
            }
        else
            {
            ptr.Append( aEntries[i]->Address().EmailAddress() );
            ptr.Append( KAddressDelimeterSemiColon );
            }

        // append whitespace, if not in the last entry
        if ( i < count - 1 )
            {
            ptr.Append( KEmptySpace );
            }
        }

    return buf;
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::GetFormattedAddressListL()
// -----------------------------------------------------------------------------
//
HBufC* CESMRNcsAifEditor::GetFormattedAddressListL(
    RPointerArray<CESMRNcsAifEntry>& aEntries,
    TBool aDisplayList ) const
    {
    FUNC_LOG;
    HBufC* buf = GetFormattedAddressListLC( aEntries, aDisplayList );
    CleanupStack::Pop( buf );
    return buf;
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::CalculateAddressListLength()
// -----------------------------------------------------------------------------
//
TInt CESMRNcsAifEditor::CalculateAddressListLength(
    RPointerArray<CESMRNcsAifEntry>& aEntries,
    TBool aDisplayList ) const
    {
    FUNC_LOG;
    TInt length = 0;
    TInt count = aEntries.Count();
    for( TInt i = 0; i < count; i++ )
        {
        CESMRNcsAifEntry* aEntry = aEntries[ i ];
        if ( !aEntry  ) continue;
        if ( aDisplayList )
            {
            length += aEntry->Length();
            }
        else
            {
            // +1 is for semicolon
            length += aEntry->Address().EmailAddress().Length() + 1;
            }
        }

    // add one white space after that so the format is
    // aa(at)bb.com; cc(at)dd.com; ee(at)ff.com
    if( count > 1 )
        {
        // ( count - 1 ) we do need white space after the last address
        length += count - 1 ;
        }

    if( aEntries.Count() > 0 )
        {
        length += 2;
        }

    return length;
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::UpdateAddressAutoCompletionL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::UpdateAddressAutoCompletionL()
    {
    FUNC_LOG;
    HBufC* text = GetNonEntryTextLC();

    if( text )
        {
        iAddressPopupList->UpdatePopupContactListL( *text, EFalse );
        CleanupStack::PopAndDestroy( text );
        }
    else
        {
        iAddressPopupList->UpdatePopupContactListL( KNullDesC, EFalse );
        }
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::UpdateAddressAutoCompletionL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::UpdateAddressAutoCompletionL(
    const TCursorSelection& aSelection )
    {
    FUNC_LOG;
    TInt length = aSelection.Length();
    HBufC* text = HBufC::NewLC( length );
    TPtr ptr = text->Des();
    Text()->Extract( ptr, aSelection.LowerPos(), length );
    ptr.Trim();
    if ( text->Length() )
        {
        iAddressPopupList->UpdatePopupContactListL( *text, EFalse );
        }
    else
        {
        iAddressPopupList->ClosePopupContactListL();
        }
    CleanupStack::PopAndDestroy( text );
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::UpdateAddressListAllL()
// -----------------------------------------------------------------------------
//
void CESMRNcsAifEditor::UpdateAddressListAllL()
    {
    FUNC_LOG;
    iAddressPopupList->UpdatePopupContactListL( KNullDesC, ETrue );
    }

void CESMRNcsAifEditor::UpdateDuplicateEntryMarkings()
    {
    FUNC_LOG;
    const TInt entryCount = iArray.Count();
    for ( TInt ii = entryCount - 1; ii >= 0; --ii )
        {
        TBool duplicateFound = EFalse;
        for ( TInt jj = entryCount - 1; jj >= 0; --jj )
            {
            if ( ii != jj && iArray[ii]->IsSameDN( *iArray[jj] ) )
                {
                duplicateFound = ETrue;
                iArray[jj]->SetDup( ETrue );
                }
            }
        iArray[ii]->SetDup( duplicateFound );
        }
    }

// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::CopyToClipboardL
// -----------------------------------------------------------------------------
void CESMRNcsAifEditor::CopyToClipboardL( const TDesC &aBuf )
    {
    FUNC_LOG;
    CClipboard* cb = CClipboard::NewForWritingLC( CCoeEnv::Static()->FsSession() );

    cb->StreamDictionary().At( KClipboardUidTypePlainText );

    CPlainText* plainText = CPlainText::NewL();
    CleanupStack::PushL( plainText );

    plainText->InsertL( 0 , aBuf );

    plainText->CopyToStoreL( cb->Store(), cb->StreamDictionary(), 0, plainText->DocumentLength() );

    CleanupStack::PopAndDestroy( plainText );
    cb->CommitL();
    CleanupStack::PopAndDestroy( cb );
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::HandleContactDeletionL
// -----------------------------------------------------------------------------
void CESMRNcsAifEditor::HandleContactDeletionL()
    {
    FUNC_LOG;
    const TInt count(iArray.Count());
    for (TInt i=count-1;i>=0;--i)
        {
        CESMRNcsAifEntry* entry = iArray[i];
        if ( FindTextL( &entry->DisplayString(), 0, 0 ) == KErrNotFound )
            {
            iArray.Remove(i);
            delete entry;
            }
        }
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::HandleTextUpdateL
// -----------------------------------------------------------------------------
void CESMRNcsAifEditor::HandleTextUpdateL()
    {
    FUNC_LOG;
    RecalculateEntryPositions();
    TCursorSelection textSelection = NonEntryTextAtPos( CursorPos() );
    if ( textSelection.Length() )
        {
        // Check non-entry text for complete entries.
        HandleTextUpdateL( textSelection );
        }
    UpdateAddressAutoCompletionL( textSelection );
    }
// -----------------------------------------------------------------------------
// CESMRNcsAifEditor::HandleTextUpdateL
// -----------------------------------------------------------------------------
void CESMRNcsAifEditor::HandleTextUpdateL( TCursorSelection& aSelection )
    {
    FUNC_LOG;
    TInt firstCharacter = aSelection.LowerPos();
    TInt lastCharacter = aSelection.HigherPos();
    for ( TInt ii = firstCharacter; ii <= lastCharacter; ++ii )
        {
        TChar character = CharAtPos( ii );
        if ( IsSentinel( character ) )
            {
            if ( character == KComma )
                {
                // Replace comma with semicolon
                CPlainText* text = Text();
                text->DeleteL( ii, 1 );
                text->InsertL( ii, KSemiColon );
                }

            // Create new entry.
            AddEntryL( firstCharacter, ii );
            TCursorSelection tmpSelection = NonEntryTextAtPos( CursorPos() );
            firstCharacter = tmpSelection.LowerPos();
            lastCharacter = tmpSelection.HigherPos();
            }
        }
    aSelection.SetSelection( lastCharacter, firstCharacter );

    }

// ---------------------------------------------------------------------------
// Handles navigation event.
// ---------------------------------------------------------------------------
//
void CESMRNcsAifEditor::HandleNavigationEventL()
    {
    FUNC_LOG;
    // Close the contact popup when cursor is moved withing the field to make it less distracting.
    // It's reopened when user types something.
    iAddressPopupList->ClosePopupContactListL();
    }

// ---------------------------------------------------------------------------
// Gets the range of non-entry text at the given position.
// ---------------------------------------------------------------------------
//
TCursorSelection CESMRNcsAifEditor::NonEntryTextAtPos( TUint aPosition ) const
    {
    FUNC_LOG;
    TCursorSelection text( TextLength(), 0 );
    for ( TInt ii = iArray.Count() - 1; ii >= 0; --ii )
        {
        if ( iArray[ii]->Includes( aPosition - 1) )
            {
            // Given position is included in existing entry
            text.SetSelection( 0, 0 );
            break;
            }
        else if ( iArray[ii]->LowerPos() >= aPosition )
            {
            // Found entry after the given position
            text.iCursorPos = iArray[ii]->LowerPos();
            }
        else if ( iArray[ii]->HigherPos() < aPosition )
            {
            // Found first entry before given position
            text.iAnchorPos = iArray[ii]->HigherPos();
            break;
            }
        }
    return text;
    }

// ---------------------------------------------------------------------------
// Gets the range of text immediatelly before given position that does not
// belong to any entry.
// ---------------------------------------------------------------------------
//
TCursorSelection CESMRNcsAifEditor::NonEntryTextBeforePos( TUint aPosition ) const
    {
    FUNC_LOG;
    TCursorSelection text( aPosition, 0 );
    for ( TInt ii = iArray.Count() - 1; ii >= 0; --ii )
        {
        if ( iArray[ii]->Includes( aPosition - 1 ) )
            {
            // Given position is included in existing entry
            text.SetSelection( 0, 0 );
            break;
            }
        else if ( iArray[ii]->HigherPos() < aPosition )
            {
            // Found first existing entry before given position
            text.SetSelection( aPosition, iArray[ii]->HigherPos() );
            break;
            }
        }
    return text;
    }

// ---------------------------------------------------------------------------
// Checks whether given character is considered as sentinel.
// ---------------------------------------------------------------------------
//
TBool CESMRNcsAifEditor::IsSentinel( TChar aCharacter ) const
    {
    FUNC_LOG;
    return ( aCharacter == KSemiColon ||
        aCharacter == KComma );
    }

// ---------------------------------------------------------------------------
// Checks whether given event is considered as navigation event.
// ---------------------------------------------------------------------------
//
TBool CESMRNcsAifEditor::IsNavigationKey( const TKeyEvent& aKeyEvent ) const
    {
    FUNC_LOG;
    return ( aKeyEvent.iCode == EKeyLeftArrow ||
             aKeyEvent.iCode == EKeyRightArrow ||
             aKeyEvent.iCode == EKeyUpArrow ||
             aKeyEvent.iCode == EKeyDownArrow );
    }

// ---------------------------------------------------------------------------
// Creates new entry from the specified section of the the input field.
// ---------------------------------------------------------------------------
//
void CESMRNcsAifEditor::AddEntryL( TInt aStart, TInt aEnd )
    {
    FUNC_LOG;
    // Get the text for the entry (excluding the delimiter)
    TInt length = aEnd - aStart;
    HBufC* text = HBufC::NewLC( length );
    TPtr ptr = text->Des();
    Text()->Extract( ptr, aStart, length );
    ptr.Trim();
    if ( text->Length() )
        {
        AddAddressL( KNullDesC(), *text );
        }
    CleanupStack::PopAndDestroy( text );
    }
// End of File

