meetingrequest/mrgui/mrfieldbuilderpluginextension/src/cesmrncsaifeditor.cpp
changeset 0 8466d47a6819
child 12 4ce476e64c59
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/meetingrequest/mrgui/mrfieldbuilderpluginextension/src/cesmrncsaifeditor.cpp	Thu Dec 17 08:39:21 2009 +0200
@@ -0,0 +1,1318 @@
+/*
+* Copyright (c) 2007-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>
+
+#ifndef FF_CMAIL_INTEGRATION
+#include <txtclipboard.h>
+#endif // FF_CMAIL_INTEGRATION
+
+#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();
+        }
+    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();
+        }
+
+    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
+