emailuis/emailui/src/ncsaifeditor.cpp
changeset 0 8466d47a6819
child 1 12c456ceeff2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emailuis/emailui/src/ncsaifeditor.cpp	Thu Dec 17 08:39:21 2009 +0200
@@ -0,0 +1,1608 @@
+/*
+* Copyright (c) 2007 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 CNcsAifEntry, CNcsAifEditor. 
+*
+*/
+
+
+
+#include "emailtrace.h"
+#include <AknsDrawUtils.h>
+#include <s32mem.h>
+#include <txtrich.h>
+#include <baclipb.h>
+#include <PtiDefs.h>
+#include <StringLoader.h>
+#include <FreestyleEmailUi.rsg>
+
+#include "ncsaifeditor.h"
+#include "ncsconstants.h"
+#include "ncsaddressinputfield.h"
+#include "ncsutility.h"
+#include "FreestyleEmailUiUtilities.h"
+#include "ncsemailaddressobject.h"
+#include "FreestyleEmailUiLayoutData.h"
+#include "FSDelayedLoader.h"
+#include "FSEmail.pan"
+
+const TChar KCharAddressDelimeterSemiColon = ';';
+const TChar KCharAddressDelimeterComma = ',';
+const TChar KCharSpace = ' ';
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::NewL
+// ---------------------------------------------------------------------------
+//
+CNcsAifEntry* CNcsAifEntry::NewL( const CNcsEmailAddressObject& aAddr )
+    {
+    FUNC_LOG;
+    CNcsAifEntry* self = new (ELeave) CNcsAifEntry;
+    CleanupStack::PushL(self);
+    self->ConstructL( aAddr );
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::NewL
+// ---------------------------------------------------------------------------
+//
+CNcsAifEntry* CNcsAifEntry::NewL(
+    const TDesC& aDn, 
+    const TDesC& aEml,
+    TBool aDisplayFull )
+    {
+    FUNC_LOG;
+    CNcsAifEntry* self = new ( ELeave ) CNcsAifEntry;
+    CleanupStack::PushL( self );
+    self->ConstructL( aDn, aEml, aDisplayFull );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::~CNcsAifEntry
+// ---------------------------------------------------------------------------
+//
+CNcsAifEntry::~CNcsAifEntry()
+    {
+    FUNC_LOG;
+    delete iAddress;
+    delete iDisplayString;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::CNcsAifEntry
+// ---------------------------------------------------------------------------
+//
+CNcsAifEntry::CNcsAifEntry() 
+    {
+    FUNC_LOG;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::ConstructL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEntry::ConstructL( const TDesC& aDn, const TDesC& aEml, TBool aDisplayFull )
+    {
+    FUNC_LOG;
+    iAddress = CNcsEmailAddressObject::NewL( aDn, aEml );
+    iAddress->SetDisplayFull( aDisplayFull );
+    ConstructL();
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::ConstructL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEntry::ConstructL( const CNcsEmailAddressObject& aAddress )
+    {
+    FUNC_LOG;
+    iAddress = CNcsEmailAddressObject::NewL( aAddress );
+    ConstructL();
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::ConstructL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEntry::ConstructL() 
+    {
+    FUNC_LOG;
+    SetDisplayStringL();
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::SetDisplayStringL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEntry::SetDisplayStringL() 
+    {
+    FUNC_LOG;
+    delete iDisplayString;
+    iDisplayString = NULL;
+
+    const TDesC& dname = iAddress->DisplayName();
+    const TDesC& email = iAddress->EmailAddress();
+    
+    TInt dnameLength = dname.Length();
+    TInt emailLength = email.Length();
+    
+    TBool displayFull = iAddress->DisplayFull() || iIsDup;
+    
+    TInt length;
+    // Show only display name OR email address if showing both is not required
+    // or if display name doesn't contain anything but the email address
+    // or if the display name is empty
+    if ( !displayFull ||
+         dname == email ||
+         !dnameLength )
+        {
+        length = dnameLength > 0 ? dnameLength : emailLength;
+        length += KEmailAddressSeparator().Length(); // ';'
+        
+        iDisplayString = HBufC::NewL( length );
+        TPtr ptr = iDisplayString->Des();
+        
+        ptr.Append( dname.Length() > 0 ? dname : email );
+        ptr.Append( KEmailAddressSeparator );
+        }
+    
+    // Otherwise, show both display name and email addresss
+    else 
+        {
+        // Display, Name;
+        length = dnameLength + emailLength + 
+            KSpace().Length() + 
+            KEmailAddressDecorationHead().Length() +
+            KEmailAddressDecorationTail().Length() +
+            KEmailAddressSeparator().Length();
+        
+        iDisplayString = HBufC::NewL( length );
+        TPtr ptr = iDisplayString->Des();
+        
+        ptr.Append( dname );
+        ptr.Append( KSpace );
+        ptr.Append( KEmailAddressDecorationHead );
+        ptr.Append( email );
+        ptr.Append( KEmailAddressDecorationTail );
+        ptr.Append( KEmailAddressSeparator );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::SetDupL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEntry::SetDupL( TBool aDup ) 
+    {
+    FUNC_LOG;
+    if ( iIsDup != aDup )
+        {
+        iIsDup = aDup;
+        // Display string needs to be recreated unless there's no
+        // meaningful display name
+        if ( iAddress->DisplayName().Length() &&
+             iAddress->DisplayName() != iAddress->EmailAddress() )
+            {
+            SetDisplayStringL();
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEntry::IsSameDN
+// ---------------------------------------------------------------------------
+//
+TBool CNcsAifEntry::IsSameDN( const CNcsAifEntry& entry ) const
+    {
+    FUNC_LOG;
+    const TDesC& ownDn = Address().DisplayName();
+    const TDesC& otherDn = entry.Address().DisplayName();
+    return ownDn.Compare( otherDn ) == 0;
+    }
+
+// ---------------------------------------------------------------------------
+// constructor/destructor
+// ---------------------------------------------------------------------------
+//
+CNcsAifEditor::CNcsAifEditor(
+    MNcsFieldSizeObserver* aSizeObserver ) 
+    : CNcsEditor( aSizeObserver, ETrue, ENcsEditorAddress ), iAddressPopupList( NULL ),
+    iAddLeftover( ETrue )
+    {
+    FUNC_LOG;
+	SetEdwinObserver( this );
+    }
+
+// ---------------------------------------------------------------------------
+// second phase constructor
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::ConstructL( const CCoeControl* aParent,
+        TInt aNumberOfLines,
+        TInt aTextLimit )
+    {
+    FUNC_LOG;
+    CNcsEditor::ConstructL( aParent, aNumberOfLines, aTextLimit );
+    iAsyncCallBack = new (ELeave) CAsyncCallBack( CActive::EPriorityStandard );
+    }
+
+// ---------------------------------------------------------------------------
+// destructor
+// ---------------------------------------------------------------------------
+//
+CNcsAifEditor::~CNcsAifEditor()
+    {
+    FUNC_LOG;
+	iArray.ResetAndDestroy();
+	iAddressArray.Reset();
+	if ( iAsyncCallBack )
+	    {
+	    iAsyncCallBack->Cancel();
+	    delete iAsyncCallBack;
+	    }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::CursorLineNumber() const
+// -----------------------------------------------------------------------------
+//
+TInt CNcsAifEditor::CursorLineNumber() const
+    {
+    FUNC_LOG;
+    
+    TInt ret = iLayout->GetLineNumber( CursorPos() );
+    ret++;
+    return ret;    
+    }
+    
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::LineCount() const
+// -----------------------------------------------------------------------------
+//    
+TInt CNcsAifEditor::LineCount() const
+    {
+    FUNC_LOG;
+    TInt lineCount = iLayout->GetLineNumber( TextLength() );
+    lineCount++;
+    return lineCount;
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::OfferKeyEventL()
+// -----------------------------------------------------------------------------
+//
+TKeyResponse CNcsAifEditor::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
+    // This is done before select since 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 );
+        }
+    
+    //when press a key down, record the coursor position
+    if ( aType == EEventKeyDown )
+    	{
+    	iLastTimeCursorPos = CursorPos();
+    	}
+
+    if ( ret == EKeyWasNotConsumed )
+        {
+        // enter completes the address entry
+        if( aType == EEventKey && (aKeyEvent.iCode == EKeyEnter || 
+        	aKeyEvent.iScanCode == EStdKeyEnter) )
+        	{
+        	// make sure there is really some text inputted 
+            TInt cursorPos( CursorPos() );
+        	
+            TCursorSelection selection = NonEntryTextAtPos( cursorPos );
+            
+            TInt length( selection.Length() );
+            
+            HBufC* text = HBufC::NewLC( length );
+            TPtr ptr = text->Des();
+            Text()->Extract( ptr, selection.LowerPos(), length );
+            ptr.Trim();
+            
+            // complete the entry by adding a semicolon, 
+            // address will be added in HandleTextUpdateL
+            if( ptr.Length() > 0 )
+            	{            
+				Text()->InsertL( cursorPos, KCharAddressDelimeterSemiColon );
+				HandleTextChangedL();
+				SetCursorPosL( cursorPos + 1, EFalse );
+            	}
+            
+            CleanupStack::PopAndDestroy( text );            
+        	}
+        
+        ret = CNcsEditor::OfferKeyEventL( aKeyEvent, aType );
+        }
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::HandleEdwinEventL()
+// This function gets called if a character is entered through the FEP.
+// Otherwise the character entry is added through OfferKeyEvent
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::HandleEdwinEventL( CEikEdwin* /*aEdwin*/,
+    TEdwinEvent aEventType )
+    {
+    FUNC_LOG;
+    if ( aEventType == MEikEdwinObserver::EEventTextUpdate )
+        {
+        // Remove any invalid entries. This is needed when entries have been marked
+        // and have got replaced with some key event handled by FEP.
+        CheckAndRemoveInvalidEntriesL();
+        
+        // Make a deferred call to HandleTextUpdateL() because it may result in
+        // changing the text field contents, and doing so directly within HandleEdwinEventL
+        // causes problems for the FEP in some special cases.
+        HandleTextUpdateDeferred();
+        }
+    else if ( aEventType == MEikEdwinObserver::EEventNavigation )
+        {
+        HandleNavigationEventL();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::SetEditorSelectionL()
+// -----------------------------------------------------------------------------
+//   
+TKeyResponse CNcsAifEditor::SetEditorSelectionL( const TKeyEvent& aKeyEvent,
+    TEventCode aType )
+    {
+    FUNC_LOG;
+    TKeyResponse response = EKeyWasNotConsumed;
+    CNcsAifEntry* 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 )
+        {
+    	// make sure there is really some text inputted 
+        TInt cursorPos( CursorPos() );
+    	
+        TCursorSelection selection = NonEntryTextAtPos( cursorPos );
+        
+        TInt length( selection.Length() );
+        
+        HBufC* text = HBufC::NewLC( length );
+        TPtr ptr = text->Des();
+        
+        if( selection.LowerPos() >= 0 )
+        	{
+			Text()->Extract( ptr, selection.LowerPos(), length );
+			ptr.Trim();
+			
+			// complete the entry
+			if( ptr.Length() > 0 )
+				{
+				Text()->InsertL( selection.HigherPos(), KCharAddressDelimeterSemiColon );
+				HandleTextChangedL();
+				HandleTextUpdateL( TCursorSelection(selection.LowerPos(), selection.HigherPos() + 1) );
+				}
+        	}
+		
+		CleanupStack::PopAndDestroy( text );
+		
+        response = CNcsEditor::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;
+                }
+            }
+        }
+    // to fix problems with updating CBA when hash key is pressed and hold
+    else if ( aKeyEvent.iScanCode == EStdKeyHash ) 
+        {
+        iAddressPopupList->ClosePopupContactListL();
+        }
+
+    // Close the address popup if we handled the event
+    if ( response == EKeyWasConsumed )
+        {
+        iAddressPopupList->ClosePopupContactListL();
+        }
+    
+    return response;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::HandleContactDeletionL()
+// ---------------------------------------------------------------------------
+//
+TKeyResponse CNcsAifEditor::HandleContactDeletionL( const TKeyEvent& aKeyEvent,
+    TEventCode aType )
+    {
+    FUNC_LOG;
+    TKeyResponse response  = EKeyWasNotConsumed;
+    if ( SelectionLength() && aType == EEventKey 
+        && IsCharacterKey( 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.
+            UpdateDuplicateEntryMarkingsL();
+    
+            // Set the cursor after the entry before the ones we just deleted
+            CNcsAifEntry* 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;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::DoCharChangeL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::DoCharChangeL()
+	{
+    FUNC_LOG;
+	RecalculateEntryPositions();
+
+	TChar previousChar = CharAtPos( CursorPos() - 1 );
+	TBool sentinel = ( previousChar == KCharAddressDelimeterSemiColon || 
+	    previousChar == KCharAddressDelimeterComma );
+	if ( sentinel )
+        {
+        // if comma was pressed we replace it with semicolon
+		if ( previousChar == KCharAddressDelimeterComma )
+            {
+			CPlainText* text = Text();
+			text->DeleteL( CursorPos() - 1, 1 );
+			text->InsertL( CursorPos() - 1, KCharAddressDelimeterSemiColon );
+            }
+		ParseNewAddressL();
+        }
+	UpdateAddressAutoCompletionL();
+	}
+
+// ---------------------------------------------------------------------------
+// CNcsAddressInputField::CharAtPos
+// ---------------------------------------------------------------------------
+//
+TChar CNcsAifEditor::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;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::SetAddressesL()
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::SetAddressesL( const RPointerArray<CNcsEmailAddressObject>& aAddresses )
+    {
+    FUNC_LOG;
+    iArray.Reset();
+    AppendAddressesL( aAddresses );
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::AppendAddressesL()
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::AppendAddressesL( const RPointerArray<CNcsEmailAddressObject>& aAddresses )
+    {
+    FUNC_LOG;
+    // First, add all the addresses without updating the editor text contents 
+    for ( TInt i=0 ; i<aAddresses.Count() ; i++ )
+        {
+    	AddAddressL( *aAddresses[i], EFalse );
+        }
+    // Update editor text content after all the items have been added
+    RepositionEntriesL( NULL );
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::GetAddressesL()
+// -----------------------------------------------------------------------------
+//   
+const RPointerArray<CNcsEmailAddressObject>& CNcsAifEditor::GetAddressesL()
+    {
+	// Clear the existing array since it may be out of sync
+	iAddressArray.Reset();
+
+	for ( TInt i=0 ; i<iArray.Count() ; i++ ) 
+        {
+		iAddressArray.AppendL(&iArray[i]->Address());
+        }
+
+	return iAddressArray;
+    }
+    
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::GetEntryAt()
+// -----------------------------------------------------------------------------
+//   
+CNcsAifEntry* CNcsAifEditor::GetEntryAt( 
+    TInt aPos, 
+    TEntryDirection aDirection ) const
+    {
+    FUNC_LOG;
+    const TChar KSpace( ' ' );
+
+	for( TInt i = 0; i < iArray.Count(); i++ )
+        {
+        CNcsAifEntry* 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;
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::GetPreviousEntryFrom()
+// -----------------------------------------------------------------------------
+//
+CNcsAifEntry* CNcsAifEditor::GetPreviousEntryFrom( TInt aPos ) const
+    {
+    FUNC_LOG;
+    CNcsAifEntry* entry = NULL;
+    
+    for( TInt i = 0 ; i < iArray.Count() ; i++ )
+        {
+        if ( iArray[i]->End() < aPos )
+            {
+            entry = iArray[i];
+            }
+        else
+            {
+            break;
+            }
+        }
+    
+    return entry;
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::CheckAddressWhenFocusLostL()
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::CheckAddressWhenFocusLostL()
+    {
+    FUNC_LOG;
+	ParseNewAddressL();
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::ParseNewAddressL()
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::ParseNewAddressL()
+	{
+    FUNC_LOG;
+	HBufC* text = GetNonEntryTextLC();
+	__ASSERT_ALWAYS( text, Panic(EFSEmailUiNullPointerException) );
+
+	if ( text->Length() )
+		{
+        // if changing focus leftover text is parsed to email
+        // object - we don't need to add it anymore
+        iAddLeftover = EFalse;
+        // check if there is a name for the email address
+        HBufC* name = CFsDelayedLoader::InstanceL()->GetContactHandlerL()->GetLastSearchNameL( *text );
+	if ( name )
+		{
+            CleanupStack::PushL( name );
+		AddAddressL( *name, *text, ETrue );
+		CleanupStack::PopAndDestroy( name );
+		}
+	else
+		{
+		AddAddressL( KNullDesC, *text );
+		}
+	    }
+	
+	CleanupStack::PopAndDestroy(text);
+	}
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::GetNonEntryTextL()
+// This will extract any text that was entered that is not
+// part of any existing entries
+// -----------------------------------------------------------------------------
+//
+HBufC* CNcsAifEditor::GetNonEntryTextLC() const
+    {
+    FUNC_LOG;
+    
+    // "non-entry text" starts after last "entry"
+    TInt start( 0 );
+    if ( iArray.Count() > 0 )
+        {
+        start = iArray[iArray.Count() - 1]->End();
+        }
+    TInt length( TextLength() - start );
+
+    // Allocate space and extract it
+    HBufC* text = HBufC::NewLC( length );
+    TPtr ptr = text->Des();
+    Text()->Extract( ptr, start, length );
+    
+    // Wipe out possible delimiter
+    TInt pos = ptr.Locate( KCharAddressDelimeterSemiColon );
+    if ( pos != KErrNotFound )
+        {
+        ptr.Delete( pos, 1 );
+        }
+    
+    // Remove unnecessary whitespaces
+    ptr.Trim();
+    
+    INFO_1("non-entry text == %S", text);
+    return text;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::CopyEntriesToClipBoardL
+// ---------------------------------------------------------------------------
+//
+TKeyResponse CNcsAifEditor::CopyEntriesToClipboardL(
+    const TKeyEvent& aKeyEvent,
+    TEventCode aType )
+    {
+    FUNC_LOG;
+    TKeyResponse ret = EKeyWasNotConsumed;
+    // check that we are copying
+    TBool copyKeyEvent = ( aType == EEventKey && aKeyEvent.iCode == 3 &&
+                           aKeyEvent.iModifiers & EModifierCtrl &&
+                           aKeyEvent.iScanCode == EPtiKeyQwertyC );
+    TBool cutKeyEvent = ( aType == EEventKey && aKeyEvent.iCode == 24 &&
+                          aKeyEvent.iModifiers & EModifierCtrl &&
+                          aKeyEvent.iScanCode == EPtiKeyQwertyX );
+    if ( copyKeyEvent || cutKeyEvent ) 
+        {
+        RPointerArray<CNcsAifEntry> entries;
+        CleanupClosePushL( entries );
+        FindSelectedEntriesL( entries );
+        if ( entries.Count() > 0 )
+            {
+            CancelFepTransaction();
+            HBufC* formattedText = GetFormattedAddressListLC( entries, EFalse );
+            TFsEmailUiUtility::CopyToClipboardL( *formattedText );
+            CleanupStack::PopAndDestroy( formattedText );
+            
+            if ( !cutKeyEvent )
+                { // cutting needs more handling
+                ret = EKeyWasConsumed;
+                }
+            }
+        CleanupStack::PopAndDestroy( &entries );
+        }
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::FindSelectedEntriesL( )
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::FindSelectedEntriesL( RPointerArray<CNcsAifEntry>& aEntries )
+    {
+    FUNC_LOG;
+    TCursorSelection selection = Selection();
+    TInt count = iArray.Count();
+    for ( TInt i = 0; i < iArray.Count(); i++ )
+        {
+        CNcsAifEntry* entry = iArray[i];
+        if ( entry->Start() >= selection.LowerPos() &&
+             entry->End() <= selection.HigherPos() )
+            {
+            aEntries.AppendL( entry );
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::EmailAddressIndexNameBySelection( )
+// -----------------------------------------------------------------------------
+//
+const CNcsEmailAddressObject* CNcsAifEditor::EmailAddressObjectBySelection() const
+    {
+    FUNC_LOG;
+	// Find the contact the cursor is in
+	const CNcsAifEntry* aEntry = GetEntryAt(CursorPos());
+	ASSERT(aEntry != NULL);
+	return &aEntry->Address();
+    }
+
+// -----------------------------------------------------------------------------
+// CNcsAifEditor::AddAddressL()
+// -----------------------------------------------------------------------------
+//
+void CNcsAifEditor::AddAddressL( const CNcsEmailAddressObject& aAddress, TBool aUpdateEditorText /*= ETrue*/ )
+    {
+    FUNC_LOG;
+    CNcsAifEntry* entry = CNcsAifEntry::NewL( aAddress );
+    CleanupStack::PushL( entry );
+    AddAddressL( entry, aUpdateEditorText );
+    CleanupStack::Pop( entry );
+    }
+
+void CNcsAifEditor::AddAddressL( 
+    const TDesC& aDisplayName, 
+    const TDesC& aEmail,
+    TBool aDisplayFull /*= EFalse*/,
+    TBool aUpdateEditorText /*= ETrue*/ )
+    {
+    FUNC_LOG;
+    CNcsAifEntry* entry = CNcsAifEntry::NewL( aDisplayName, aEmail, aDisplayFull );
+    CleanupStack::PushL( entry );
+	AddAddressL( entry, aUpdateEditorText );
+	CleanupStack::Pop( entry );
+    }
+
+void CNcsAifEditor::AddAddressL( CNcsAifEntry* aNewEntry, TBool aUpdateEditorText )
+    {
+    FUNC_LOG;
+	TInt idx;
+	
+	// Check for duplicate display names
+	for ( idx=0 ; idx<iArray.Count() ; idx++ ) 
+        {
+		if ( iArray[idx]->IsSameDN(*aNewEntry) ) 
+            {
+			iArray[idx]->SetDupL();
+			aNewEntry->SetDupL();
+            }
+        }
+
+	// Find the location where we need to insert the address.
+	// Browse from back to forth to make last index as default index. 
+	// This ensures items remain in correct order when populating field from
+	// existing message.
+	TInt cursorPos = CursorPos();
+	
+    // if we are at the end of editor the address was
+    // added from MRU list or separator was typed in
+    if ( cursorPos == Text()->DocumentLength() )
+        {
+        iAddLeftover = EFalse;
+        }
+	
+	for ( idx = iArray.Count() ; idx > 0 ; idx-- ) 
+        {
+		if ( cursorPos >= iArray[idx-1]->End() ) break;
+        }
+	if ( idx == iArray.Count() ) 
+        {
+		// Tack the address onto the end of the array
+		iArray.AppendL( aNewEntry );
+        }
+	else
+        {
+		iArray.InsertL( aNewEntry, idx );
+        }
+	
+	if ( aUpdateEditorText )
+	    {
+	    // Trap because we must not leave after we have taken the ownership of aNewEntry.
+	    // Otherwise douple deletion might happen.
+	    TRAP_IGNORE( RepositionEntriesL( aNewEntry ) );
+	    }
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::RecalculateEntryPositions()
+// The text has changed, so recalculate the positions of the items.
+// ---------------------------------------------------------------------------
+//	
+void CNcsAifEditor::RecalculateEntryPositions()
+	{
+    FUNC_LOG;
+	// We only need to worry about items right of the cursor
+	TInt pos = CursorPos();
+	TInt error = KErrNone;
+	
+	// Find the first item to the right of the cursor
+	TInt idx = 0;
+	for ( idx = 0; idx < iArray.Count(); idx++ )
+		{
+		if ( ( iArray[idx]->Includes( iLastTimeCursorPos ) ) 
+				 || ( iArray[idx]->Start() >= iLastTimeCursorPos ) )   
+			{
+			break;
+			}
+		}	
+	
+   	// If no entry was to the right of the cursor position
+	// then the new text was added at the end of the text.
+	// Don't do anything
+	if ( idx == iArray.Count() )
+		{
+		return;
+		}
+	
+	// Find the location of the first entry to the right
+	// of the cursor using a display string match
+	pos = Min( iArray[idx]->Start(), pos );
+	TRAP( error, pos = FindTextL( &iArray[idx]->DisplayString(), pos,
+	    CEikEdwin::EFindCaseSensitive | CEikEdwin::ENoBusyMessage ) );
+	ASSERT( KErrNone == error && KErrNotFound != pos );
+
+	// Now reposition all entries to the right
+	for ( ; idx<iArray.Count(); idx++ ) 
+		{
+		pos = iArray[idx]->SetPos( pos );
+		pos++; // for whitespace
+		}
+	}
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::RepositionEntriesL()
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::RepositionEntriesL( const CNcsAifEntry* aPosEntry )
+	{
+    FUNC_LOG;
+	TInt pos = 0;
+	CNcsAifEntry* entry;
+	for ( TInt i=0 ; i<iArray.Count() ; i++ ) 
+		{
+		entry = iArray[i];
+		pos = entry->SetPos( pos );
+		pos++; // for whitespace
+		}
+
+	// Reset the text
+	SetCursorPosL( 0, EFalse ); //In case the cursor pos is invalid
+	HBufC* text = NULL;
+	text = GetFormattedAddressListLC( iArray );
+	
+    if ( iAddLeftover )
+        {
+        TInt lengthBefore = Text()->DocumentLength();
+        HBufC* textBefore = HBufC::NewLC( lengthBefore );
+        TPtr ptrBefore = textBefore->Des();
+        Text()->Extract( ptrBefore, 0, lengthBefore );
+        ptrBefore.Trim();
+        // find text after last semicolon
+        TInt colon = ptrBefore.LocateReverseF( 
+                KCharAddressDelimeterSemiColon ) + 1;
+        TPtrC leftover = ptrBefore.Mid( colon );
+        HBufC* newText = HBufC::NewLC( text->Length() + leftover.Length() );
+        TPtr newTextPtr = newText->Des();
+        // add all email addresses
+        newTextPtr.Append( text->Des() );
+        // add the text that was after last email object
+        newTextPtr.Append( leftover );
+    
+        SetTextL( newText );
+        CleanupStack::PopAndDestroy( newText );
+        CleanupStack::PopAndDestroy( textBefore );
+        }
+    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 );
+    	}
+	}
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::CheckAndRemoveInvalidEntriesL()
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::CheckAndRemoveInvalidEntriesL()
+    {
+    FUNC_LOG;
+    TInt currentCursorPos( CursorPos() );
+    const TInt KNoEntryRemoved = -1;
+    TInt removedEntryIndex( KNoEntryRemoved );
+    
+    for ( TInt i = iArray.Count() - 1 ; i >= 0 ; --i )
+        {
+        TInt matchesInText;
+        TInt matchesInArray;
+        TInt arrayItemCurPos( iArray[i]->LowerPos() );
+        
+        GetMatchingEntryCountsL( iArray[i], matchesInText, matchesInArray );
+
+        // Entry is removed if:
+        // a) there's no matches for it in the text, or
+        // b) there're less matches for it in the text than in array (i.e.,
+        //    a duplicate ("foo(at)foo.org; foo(at)foo.org") has just been removed)
+        // In b) case the correct duplicate is the one that is in current
+        // cursor position (or one off due to possible whitespace).
+        if ( 0 == matchesInText ||
+             ( matchesInText < matchesInArray &&
+               ( currentCursorPos == arrayItemCurPos || 
+                 (1 + currentCursorPos) == arrayItemCurPos ) ) )
+            {
+            delete iArray[i];
+            iArray.Remove(i);
+            removedEntryIndex = i;
+            }
+        }
+    
+    if ( KNoEntryRemoved != removedEntryIndex )
+        {
+        // at least one entry has been removed => udpates duplicate markings
+        UpdateDuplicateEntryMarkingsL();
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::GetLookupTextLC()
+// ---------------------------------------------------------------------------
+//
+HBufC* CNcsAifEditor::GetLookupTextLC() const
+    {
+    FUNC_LOG;
+	HBufC* text = GetTextInHBufL();
+	
+	if (text == NULL) return NULL;
+
+	CleanupStack::PushL( text );
+	TPtr ptr( text->Des() );
+	ptr = ptr.LeftTPtr( CursorPos() );
+	TInt location = ptr.LocateReverse( KCharAddressDelimeterSemiColon );
+	if( location != KErrNotFound )
+        {
+		ptr = ptr.RightTPtr( ptr.Length() - location -1 );
+		ptr.TrimLeft();
+        }
+	return text;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::GetFormattedAddressListLC()
+// ---------------------------------------------------------------------------
+//
+HBufC* CNcsAifEditor::GetFormattedAddressListLC(
+    RPointerArray<CNcsAifEntry>& 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( KEmailAddressSeparator );
+            }
+        
+		// append whitespace, if not in the last entry
+        if ( i < count - 1 )
+            {
+            ptr.Append( KLineFeed );
+            }
+        }
+		
+	return buf;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::GetFormattedAddressListL()
+// ---------------------------------------------------------------------------
+//
+HBufC* CNcsAifEditor::GetFormattedAddressListL(
+    RPointerArray<CNcsAifEntry>& aEntries,
+    TBool aDisplayList ) const
+	{
+    FUNC_LOG;
+    HBufC* buf = GetFormattedAddressListLC( aEntries, aDisplayList );
+    CleanupStack::Pop( buf );
+    return buf;
+	}
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::CalculateAddressListLength()
+// ---------------------------------------------------------------------------
+//
+TInt CNcsAifEditor::CalculateAddressListLength(
+    RPointerArray<CNcsAifEntry>& aEntries,
+    TBool aDisplayList ) const
+    {
+    FUNC_LOG;
+	TInt length = 0;
+	TInt count = aEntries.Count();
+	for ( TInt i = 0; i < count; i++ )
+        {
+		CNcsAifEntry* entry = aEntries[ i ];
+		if ( !entry ) continue;
+        if ( aDisplayList )
+            {
+            length += entry->Length();
+            }
+        else
+            {
+            // +1 is for semicolon
+            length += entry->Address().EmailAddress().Length() + 1;
+            }
+        }
+	
+	// add one white space after that so the format is
+	// aamiumaubb.com; ccmiumaudd.com; eemiumauff.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;
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::UpdateAddressAutoCompletionL()
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::UpdateAddressAutoCompletionL()
+    {
+    FUNC_LOG;
+	HBufC* text = GetNonEntryTextLC();
+	__ASSERT_ALWAYS( text, Panic(EFSEmailUiNullPointerException) );
+
+		iAddressPopupList->UpdatePopupContactListL( *text, EFalse );
+		CleanupStack::PopAndDestroy( text );
+        }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::UpdateAddressAutoCompletionL()
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::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 );
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::UpdateAddressListAllL()
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::UpdateAddressListAllL()
+    {
+    FUNC_LOG;
+	iAddressPopupList->UpdatePopupContactListL( KNullDesC, ETrue );
+    }
+
+
+// ---------------------------------------------------------------------------
+// Updates the duplicate markings in the entry array.
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::UpdateDuplicateEntryMarkingsL()
+    {
+    FUNC_LOG;
+    const TInt entryCount = iArray.Count();
+    for ( TInt ii = entryCount - 1; ii >= 0; --ii )
+        {
+        TBool duplicateFound = EFalse;
+        for ( TInt jj = ii - 1; jj >= 0; --jj )
+            {
+            if ( iArray[ii]->IsSameDN( *iArray[jj] ) )
+                {
+                duplicateFound = ETrue;
+                iArray[jj]->SetDupL( ETrue );
+                }
+            }
+        iArray[ii]->SetDupL( duplicateFound );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Makes a deferred call to HandleTextUpdateL
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::HandleTextUpdateDeferred()
+    {
+    FUNC_LOG;
+    if ( iAsyncCallBack )
+        {
+        iAsyncCallBack->Cancel();
+        iAsyncCallBack->Set( TCallBack( DoHandleTextUpdate, this ) );
+        iAsyncCallBack->CallBack();
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Static wrapper function for HandleTextUpdateL() 
+// ---------------------------------------------------------------------------
+//
+TInt CNcsAifEditor::DoHandleTextUpdate( TAny* aSelf )
+    {
+    FUNC_LOG;
+    CNcsAifEditor* self = static_cast<CNcsAifEditor*>( aSelf );
+    TRAPD( err, self->HandleTextUpdateL() );
+    return err;
+    }
+
+// ---------------------------------------------------------------------------
+// Handles text update.
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::HandleTextUpdateL()
+    {
+    FUNC_LOG;
+    RecalculateEntryPositions();
+    TCursorSelection textSelection = NonEntryTextAtPos( CursorPos() );
+    TBool newEntryCreated = EFalse;
+    if ( textSelection.Length() )
+        {
+        // Check non-entry text for complete entries.
+        newEntryCreated = HandleTextUpdateL( textSelection );
+        }
+    
+    if ( newEntryCreated )
+        {
+        iAddressPopupList->ClosePopupContactListL();
+        
+        // add line feed after new entry
+        TInt cursorPos( CursorPos() );
+        Text()->InsertL( cursorPos, TChar(CEditableText::ELineBreak) );
+        HandleTextChangedL();
+    	SetCursorPosL( cursorPos + 1, EFalse );
+        }
+    else
+        {
+        UpdateAddressAutoCompletionL( textSelection );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CNcsAifEditor::HandleTextUpdateL()
+// ---------------------------------------------------------------------------
+//
+TBool CNcsAifEditor::HandleTextUpdateL( const TCursorSelection& aSelection )
+    {
+    FUNC_LOG;
+    iAddLeftover = ETrue;
+    TInt firstCharacter = aSelection.LowerPos();
+    TInt lastCharacter = aSelection.HigherPos();
+    
+    // get the inputted text
+    TInt length = lastCharacter - firstCharacter;
+    
+    HBufC* text = HBufC::NewLC( length );
+    TPtr ptr = text->Des();
+    Text()->Extract( ptr, firstCharacter, length );
+    ptr.Trim();
+    
+    TBool entriesFound( EFalse );
+    
+    // start looking for entries separated with semicolon
+    TInt start( 0 );
+    TInt end( ptr.Length() );
+    
+    for ( TInt ii = 0; ii < end; ++ii )
+        {
+        TChar character = ptr[ii];
+        
+        if ( IsSentinel( character ) )
+            {
+            if ( character == KCharAddressDelimeterComma )
+                {
+                // Replace comma with semicolon
+                ptr[ii] = KCharAddressDelimeterSemiColon;
+                }
+
+            // Create new entry.
+            if ( start < end )
+                {
+                // only if longer than 0, if not we'll get 
+                // "empty" email address
+                if ( ii-start )
+                    {
+                    AddAddressL( KNullDesC(), ptr.Mid(start, ii-start) );
+                    start = Min( ii + 1, end );
+                    entriesFound = ETrue;
+                    }
+                }
+            }
+        }
+    
+    CleanupStack::PopAndDestroy( text );
+        
+    return entriesFound;
+    }
+
+// ---------------------------------------------------------------------------
+// Handles navigation event.
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::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 CNcsAifEditor::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;
+            }
+        }
+
+    // get the selected text to remove whitespace
+    TInt length( text.Length() );    
+    
+    HBufC* selectedText = NULL;
+    TRAPD( err, selectedText = HBufC::NewL( length ) );
+    
+    if( err == KErrNone )
+    	{
+		TPtr ptr = selectedText->Des();
+		Text()->Extract( ptr, text.LowerPos(), length );
+		
+		// trim from end
+		TInt index( length - 1 );
+		
+		while( index >= 0 && IsWhitespace( ptr[index--] ) )
+			{
+			text.iCursorPos--;
+			}
+		
+		// trim from begin
+		index = 0;
+		
+		while( index < length && IsWhitespace( ptr[index++] ) )
+			{
+			text.iAnchorPos++;
+			}
+
+		delete selectedText;
+		selectedText = NULL;
+    	}    
+    	
+    return text;
+    }
+
+// ---------------------------------------------------------------------------
+// Gets the range of text immediatelly before given position that does not
+// belong to any entry. 
+// ---------------------------------------------------------------------------
+//
+TCursorSelection CNcsAifEditor::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 CNcsAifEditor::IsSentinel( TChar aCharacter ) const
+    {
+    FUNC_LOG;
+    return ( aCharacter == KCharAddressDelimeterSemiColon || 
+        aCharacter == KCharAddressDelimeterComma );
+    }
+
+// ---------------------------------------------------------------------------
+// Checks whether given character is considered as whitespace.
+// ---------------------------------------------------------------------------
+//
+TBool CNcsAifEditor::IsWhitespace( TChar aCharacter ) const
+    {
+    FUNC_LOG;
+    return ( aCharacter == KCharSpace || 
+    		 aCharacter == TChar(CEditableText::ELineBreak) || 
+    		 aCharacter == TChar(CEditableText::EParagraphDelimiter) );
+    }
+
+// ---------------------------------------------------------------------------
+// Checks whether given event is considered as navigation event.
+// ---------------------------------------------------------------------------
+//
+TBool CNcsAifEditor::IsNavigationKey( const TKeyEvent& aKeyEvent ) const
+    {
+    FUNC_LOG;
+    return ( aKeyEvent.iCode == EKeyLeftArrow ||
+             aKeyEvent.iCode == EKeyRightArrow ||
+             aKeyEvent.iCode == EKeyUpArrow ||
+             aKeyEvent.iCode == EKeyDownArrow ||
+             aKeyEvent.iScanCode == EStdKeyLeftArrow ||
+             aKeyEvent.iScanCode == EStdKeyRightArrow ||
+             aKeyEvent.iScanCode == EStdKeyUpArrow ||
+             aKeyEvent.iScanCode == EStdKeyDownArrow );
+    }
+
+// ---------------------------------------------------------------------------
+// Checks whether given event is one which generates visible character
+// ---------------------------------------------------------------------------
+//
+TBool CNcsAifEditor::IsCharacterKey( const TKeyEvent& aKeyEvent ) const
+    {
+    FUNC_LOG;
+    TUint ctrlModifiers = EModifierLeftCtrl | EModifierRightCtrl | EModifierCtrl;
+    TBool ctrlEvent = aKeyEvent.iModifiers & ctrlModifiers;
+    TBool isAppKey = ( aKeyEvent.iScanCode >= EStdKeyApplication0) && (aKeyEvent.iScanCode <= EStdKeyKeyboardExtend);
+    return ( !ctrlEvent && !IsNavigationKey(aKeyEvent) && !isAppKey  );
+    }
+
+// ---------------------------------------------------------------------------
+// Gets the count of substrings (in current text field) matching the aEntry's
+// DisplayName (DN) and the count of matching (same DN) items in iArray. 
+// ---------------------------------------------------------------------------
+//
+void CNcsAifEditor::GetMatchingEntryCountsL(  const CNcsAifEntry* aEntry,
+                                              TInt& aNrOfMatchesInText,
+                                              TInt& aNrOfMatchesInEntryArray )
+    {
+    aNrOfMatchesInText = 0;
+    aNrOfMatchesInEntryArray = 0;
+    TInt pos( 0 );
+    const TInt end_pos( TextLength() );
+
+    // First a checking loop for finding the number of matching substrings
+    // (i.e., substrings that match the entry's displaystring) in current text
+    while ( pos < end_pos )
+        {
+        pos = FindTextL( &aEntry->DisplayString(), pos, 
+                         CEikEdwin::EFindCaseSensitive | 
+                         CEikEdwin::ENoBusyMessage );
+
+        // No more matches for entry found => checking finished for this one 
+        if ( pos < 0 )
+            {
+            pos = end_pos; // ends the loop
+            }
+        // Match found => update counter
+        else
+            {
+            ++aNrOfMatchesInText;
+            // Move to next word
+            TInt len;
+            TInt startPos;
+            GetWordInfo( pos, startPos, len );
+            pos = startPos+len;
+            }
+        }
+
+    // Secondly check the number of entries in entry array that match
+    // the given entry's displayname.
+    for ( TInt i = iArray.Count()-1; i >= 0; --i )
+        {
+        if ( !aEntry->DisplayString().Compare( iArray[i]->DisplayString() ) )
+            {
+            ++aNrOfMatchesInEntryArray;
+            }
+        }
+    }
+
+// End of File
+