messagingappbase/msgeditor/viewsrc/MsgAddressControl.cpp
branchRCL_3
changeset 60 7fdbb852d323
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingappbase/msgeditor/viewsrc/MsgAddressControl.cpp	Wed Sep 01 12:31:54 2010 +0100
@@ -0,0 +1,1339 @@
+/*
+* Copyright (c) 2002-2006 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:  MsgAddressControl implementation
+*
+*/
+
+
+
+// ========== INCLUDE FILES ================================
+
+#include <eikenv.h>                        // for CEikonEnv
+#include <eikappui.h>                      // for CEikAppUi
+#include <barsread.h>                      // for TResourceReader
+#include <eikedwin.h>                      // for TClipboardFunc
+#include <txtrich.h>                       // for CRichText
+#include <AknUtils.h>                      // for AknUtils
+#include <aknbutton.h>                     // for CAknButton
+#include <eiklabel.h>                      // for CEikLabel
+
+#include "MsgAddressControl.h"             // for CMsgAddressControl
+#include "MsgAddressControlEditor.h"       // for CMsgAddressControlEditor
+#include "MsgAddressControlEditorField.h"  // for CMsgAddressControlEditorField
+#include "MsgBaseControlObserver.h"        // for MMsgBaseControlObserver
+#include "MsgRecipientItem.h"              // for CMsgRecipientItem
+#include "MsgEditorPanic.h"                // for CMsgEditor panics
+#include "MsgEditorCommon.h"               // for KArabicSemicolon
+
+// ========== EXTERNAL DATA STRUCTURES =====================
+
+// ========== EXTERNAL FUNCTION PROTOTYPES =================
+
+// ========== CONSTANTS ====================================
+
+// ========== MACROS =======================================
+
+// ========== LOCAL CONSTANTS AND MACROS ===================
+
+const TInt KRecipientArrayGranularity = 10;
+const TInt KMaxEntryLength = 256;
+const TUint KMsgListSeparator = ';';             // List separator
+
+// Unicode sign for S60 "enter" symbol
+const TInt KDownwardsArrowWithTipLeftwards  = 0x21B2;
+const TInt KDownwardsArrowWithTipRightwards = 0x21B3;
+
+
+// ========== MODULE DATA STRUCTURES =======================
+
+// ========== LOCAL FUNCTION PROTOTYPES ====================
+
+// ========== LOCAL FUNCTIONS ==============================
+
+// ========== MEMBER FUNCTIONS =============================
+
+
+// ---------------------------------------------------------
+// CMsgAddressControl::CMsgAddressControl
+//
+// Constructor.
+// ---------------------------------------------------------
+//
+CMsgAddressControl::CMsgAddressControl( MMsgBaseControlObserver& aBaseControlObserver ) : 
+    CMsgExpandableControl( aBaseControlObserver )
+    {
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::~CMsgAddressControl
+//
+// Destructor.
+// ---------------------------------------------------------
+//
+CMsgAddressControl::~CMsgAddressControl()
+    {
+    if ( iRecipientArray )
+        {
+        iRecipientArray->ResetAndDestroy();
+        }
+
+    delete iRecipientArray;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::AddRecipientL
+//
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CMsgAddressControl::AddRecipientL( const TDesC& aName,
+                                                 const TDesC& aAddress,
+                                                 TBool aVerified,
+                                                 MVPbkContactLink* aContactLink )
+    {
+    TInt count   = 0;
+    TInt size    = 0;
+    TInt entries = iAddressControlEditor->EntryCount();
+
+    GetSizeOfAddresses( count, size );
+
+    // when field is empty, count == 0 but entries == 1.
+    // when count > 0, this inserts at the end of the field.
+    if ( entries > 1 )
+        {
+        count = entries;
+        }
+
+    iAddressControlEditor->SetCursorPosAndCancelSelectionL(
+        iAddressControlEditor->TextLength() );
+    iAddressControlEditor->CheckHighlightingL();
+    iAddressControlEditor->InsertEntryL(
+        count,
+        aName,
+        aAddress,
+        aVerified,
+        ETrue,
+        aContactLink
+         );
+
+    FormatAndSetCursorPosL( iAddressControlEditor->TextLength() );
+    DrawDeferred();
+    iControlModeFlags |= EMsgControlModeModified;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::AddRecipientsL
+//
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CMsgAddressControl::AddRecipientsL(
+    const CMsgRecipientList& aRecipients )
+    {
+    CMsgRecipientArray* recipients = GetRecipientsL();
+    RefreshL( *recipients );
+
+    TInt count = 0;
+    TInt size = 0;
+    TInt entries = iAddressControlEditor->EntryCount();
+
+    GetSizeOfAddresses( count, size );
+
+    // when field is empty, count == 0 but entries == 1.
+    // when count > 0, this inserts at the end of the field.
+    if ( entries > 1 )
+        {
+        count = entries;
+        }
+
+    TInt cc = aRecipients.Count();
+    CMsgRecipientItem* item = NULL;
+
+    iAddressControlEditor->SetCursorPosAndCancelSelectionL(
+        iAddressControlEditor->TextLength() );
+    iAddressControlEditor->CheckHighlightingL();
+
+    TBool bandFormatting( EFalse );
+    if ( iAddressControlEditor->TextLayout()->IsFormattingBand() )
+        {
+        bandFormatting = ETrue;
+        }
+        
+    for ( TInt i = 0; i < cc; i++ )
+        {
+        item = aRecipients.At( i );
+        iAddressControlEditor->InsertEntryL(
+            count,
+            *item->Name(),
+            *item->Address(),
+            item->IsVerified(),
+            item->IsValidated(),
+            item->ContactLink() );
+        count++;
+        }    
+    
+    TInt pos = iAddressControlEditor->TextLength();
+
+    iAddressControlEditor->TextView()->SetDocPosL( pos, EFalse );
+    
+    FormatAndSetCursorPosL( pos );
+    
+    // Editor changed to band formatting due to address addition.
+    // Set the editor height to maximum as text was added directly to
+    // richtext and height is not updated correctly because of partial format.
+    if ( !bandFormatting &&
+         iAddressControlEditor->TextLayout()->IsFormattingBand() )
+        {
+        // Set newSize to current width and maximum body height and
+        // force CMsgExpandableControl::HandleEdwinSizeChangeEventL to
+        // update the control size.
+        iControlModeFlags |= EMsgControlModeForceSizeUpdate;
+            
+        TSize newSize( iAddressControlEditor->Size().iWidth,
+                       iMaxBodyHeight - 1 );
+        
+        iAddressControlEditor->SetSize( newSize );
+        
+        iControlModeFlags &= ~EMsgControlModeForceSizeUpdate;
+        }
+        
+    DrawDeferred();
+    iControlModeFlags |= EMsgControlModeModified;
+    }
+
+
+// ---------------------------------------------------------
+// CMsgAddressControl::GetRecipientsL
+//
+//
+// ---------------------------------------------------------
+//
+EXPORT_C CMsgRecipientArray* CMsgAddressControl::GetRecipientsL()
+    {
+    TInt entryNumber( iAddressControlEditor->EntryCount() );
+    const CMsgAddressControlEditorField* field = NULL;
+    TBuf<KMaxEntryLength> entryBuf;
+    TInt ret( KErrNotFound );
+
+    if ( iRecipientArray == NULL )
+        {
+        iRecipientArray = new ( ELeave )
+            CMsgRecipientArray( KRecipientArrayGranularity );
+        }
+    else
+        {
+        iRecipientArray->ResetAndDestroy();
+        }
+
+    for ( TInt ii = 0; ii < entryNumber; ii++ )
+        {
+        field = iAddressControlEditor->Field( ii );
+
+        if ( field && field->IsVerified() )
+            {
+            HBufC* addressCopy = field->Address()->AllocLC();
+            TPtr addrTPtr = addressCopy->Des();
+            AknTextUtils::ConvertDigitsTo( addrTPtr, EDigitTypeWestern );
+            
+            CMsgRecipientItem* item =
+                CMsgRecipientItem::NewLC( *field->Name(), *addressCopy );
+
+            const MVPbkContactLink* contactLink = field->ContactLink();
+            if ( contactLink )                  
+                {
+                item->SetContactLink( contactLink );
+                }
+                
+            item->SetVerified( field->IsVerified() );
+            item->SetValidated( field->IsValidated() );
+            iRecipientArray->AppendL( item );
+
+            CleanupStack::Pop(); // item
+            CleanupStack::PopAndDestroy(); // addressCopy
+            }
+        else
+            {
+            SetParsingInfo( ii, 0, 0 );
+            ret = KErrNone;
+            while ( ret == KErrNone )
+                {
+                ret = GetNextItemOnEntry( entryBuf );
+                if ( ( ret == KErrNone ) && ( entryBuf.Length() > 0 ) )
+                    {
+                    AknTextUtils::ConvertDigitsTo( entryBuf, EDigitTypeWestern );
+                    // unverified entries are treated as addresses.
+                    CMsgRecipientItem* item =
+                        CMsgRecipientItem::NewLC( KNullDesC, entryBuf );
+
+                    iRecipientArray->AppendL( item );
+
+                    CleanupStack::Pop(); // item
+                    entryBuf.Zero();
+                    }
+                }
+            }
+        }
+
+    ResetParsingInfo();
+    return iRecipientArray;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::ResetL
+//
+// Clears content of the address control.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::ResetL()
+    {
+    ResetParsingInfo();
+    TInt entryCount( iAddressControlEditor->EntryCount() );
+
+    iAddressControlEditor->TurnHighlightingOffL();
+    iAddressControlEditor->DeleteAllL();
+    iControlModeFlags |= EMsgControlModeModified;
+
+    return entryCount;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::Reset
+//
+// from CMsgBaseControl.
+// Same as above but non-leaving version.
+// ---------------------------------------------------------
+//
+EXPORT_C void CMsgAddressControl::Reset()
+    {
+    TRAP_IGNORE( ResetL() );
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::GetFirstUnverifiedStringL
+//
+// Finds the first unverified string and updates also the iParserInfo
+// correspondingly.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::GetFirstUnverifiedStringL( HBufC*& aString )
+    {
+    __ASSERT_DEBUG( IsFocused(), Panic( EMsgControlNotFocused ) );
+
+    ResetParsingInfo();
+    return GetNextUnverifiedStringL( aString );
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::GetNextUnverifiedStringL
+//
+// Finds the next unverified string.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::GetNextUnverifiedStringL( HBufC*& aString )
+    {
+    __ASSERT_DEBUG( IsFocused(), Panic( EMsgControlNotFocused ) );
+
+    if ( aString != NULL )
+        {
+        delete aString;
+        aString = NULL;
+        }
+
+    iAddressControlEditor->TurnHighlightingOffL();
+
+    TInt entryCount = iAddressControlEditor->EntryCount();
+    TInt entryNumber( iParsingInfo.iEntryNum );
+    TBuf<KMaxEntryLength> entryBuf;
+    TInt ret( KErrNotFound );
+    HBufC* string = NULL;
+
+    while ( entryNumber < entryCount )
+        {
+        entryBuf.Zero();
+        if ( iAddressControlEditor->IsEntryVerified( entryNumber ) )
+            {
+            entryNumber++;
+            SetParsingInfo( entryNumber, 0, 0 );
+            }
+        else
+            {
+            ret = GetNextItemOnEntry( entryBuf );
+            if ( ( ret == KErrNone ) && ( entryBuf.Length() > 0 ) )
+                {
+                string = entryBuf.AllocLC();
+                TInt firstPos = iAddressControlEditor->EntryStartPos(
+                    iParsingInfo.iEntryNum );
+                FormatAndSetCursorPosL( firstPos + iParsingInfo.iEndPos );
+                break;
+                }
+            else if ( iParsingInfo.iEndPos > iParsingInfo.iStartPos )
+                {
+                // a string could not be found but there seems to be white
+                // spaces on an entry which may be also followed by the list
+                // separator, hence clean them up
+                iAddressControlEditor->DeleteEntryCharsL(
+                    iParsingInfo.iEntryNum,
+                    iParsingInfo.iStartPos,
+                    iParsingInfo.iEndPos );
+
+                SetParsingInfo(
+                    entryNumber,
+                    iParsingInfo.iStartPos,
+                    iParsingInfo.iStartPos );
+                }
+            else if ( ( iParsingInfo.iEndPos == 0 ) &&
+                     ( iParsingInfo.iEntryNum < entryCount - 1 ) )
+                {
+                // there is just an EParagraphDelimiter on the entry
+                iAddressControlEditor->DeleteEntryL( iParsingInfo.iEntryNum );
+                SetParsingInfo( entryNumber, 0, 0 );
+                entryCount--;
+                }
+            else
+                {
+                // there are no strings left on the current entry
+                entryNumber++;
+                SetParsingInfo( entryNumber, 0, 0 );
+                }
+            }
+        }
+
+    if ( entryNumber >= entryCount )
+        {
+        FormatAndSetCursorPosL( iAddressControlEditor->TextLength() );
+        }
+
+    aString = string;
+
+    if ( string )
+        {
+        CleanupStack::Pop();  // string
+        }
+
+    return ret;
+    }
+
+
+// ---------------------------------------------------------
+// CMsgAddressControl::ReplaceUnverifiedStringL
+//
+// Replaces a string with contents of the recipient array aArray. 
+// The string is replaced according to the iParsingInfo.
+//
+// This function cannot undelete a string if it leaves after/while removing it.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::ReplaceUnverifiedStringL(
+    CMsgRecipientArray& aArray )
+    {
+    __ASSERT_DEBUG( IsFocused(), Panic( EMsgControlNotFocused ) );
+
+    if ( ( iParsingInfo.iEndPos <= iParsingInfo.iStartPos ) || 
+        ( iParsingInfo.iEntryNum >= iAddressControlEditor->EntryCount() ) )
+        {
+        return KErrNotFound;
+        }
+
+    iAddressControlEditor->TurnHighlightingOffL();
+
+    TInt entryLength = iAddressControlEditor->EntryLength( iParsingInfo.iEntryNum );
+    iAddressControlEditor->DeleteEntryCharsL(
+        iParsingInfo.iEntryNum,
+        iParsingInfo.iStartPos,
+        iParsingInfo.iEndPos );
+   
+    if ( ( iParsingInfo.iStartPos > 0 ) && ( iParsingInfo.iEndPos < entryLength ) )
+        {
+        iAddressControlEditor->InsertEntryBreakL(
+            iParsingInfo.iEntryNum,
+            iParsingInfo.iStartPos );
+        iParsingInfo.iEntryNum++;
+        }
+
+    TInt entryNumber( iParsingInfo.iEntryNum );
+    TInt contactCount = aArray.Count();
+    CMsgRecipientItem* recipientItem = NULL;
+
+    for ( TInt i = 0; i < contactCount; i++ )
+        {
+        recipientItem = aArray.At( i );
+        iAddressControlEditor->InsertEntryL(
+            entryNumber,
+            *recipientItem->Name(),
+            *recipientItem->Address(),
+            recipientItem->IsVerified(),
+            recipientItem->IsValidated(),
+            recipientItem->ContactLink() );
+            
+        entryNumber++;
+        }
+
+    SetParsingInfo( iParsingInfo.iEntryNum, 0, 0 );
+    TInt firstPos = iAddressControlEditor->EntryStartPos( iParsingInfo.iEntryNum );
+    entryLength = iAddressControlEditor->EntryLength( iParsingInfo.iEntryNum );
+    TInt endPos = firstPos + entryLength - 1;
+    SetParsingInfo( iParsingInfo.iEntryNum, endPos, endPos );
+    FormatAndSetCursorPosL( endPos );
+    iControlModeFlags |= EMsgControlModeModified;
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::RefreshL
+//
+// Refreshes the contents of address control.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::RefreshL( const CMsgRecipientArray& aArray )
+    {
+    iEditor->SetEdwinSizeObserver( NULL );
+    
+    TRAPD( error, ResetL() );  // Clears the control and turns highlighting off
+    
+    iEditor->SetEdwinSizeObserver( this );
+    
+    if ( error != KErrNone )
+        {
+        User::Leave( error );
+        }
+
+    TInt entryNumber = 0;
+    TInt contactCount = aArray.Count();
+    CMsgRecipientItem* item = NULL;
+
+    for ( TInt i = 0; i < contactCount; i++ )
+        {
+        item = aArray.At( i );
+        iAddressControlEditor->InsertEntryL(
+            entryNumber,
+            *item->Name(),
+            *item->Address(),
+            item->IsVerified(),
+            item->IsValidated(),
+            item->ContactLink() );
+        entryNumber++;
+        }
+
+    ResetParsingInfo();
+    FormatAndSetCursorPosL( iAddressControlEditor->TextLength() );
+    iControlModeFlags |= EMsgControlModeModified;
+
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::HighlightUnverifiedStringL
+//
+// Highlights the found unverified entry.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::HighlightUnverifiedStringL()
+    {
+    __ASSERT_DEBUG( IsFocused(), Panic( EMsgControlNotFocused ) );
+
+    TInt ret = iParsingInfo.iEndPos - iParsingInfo.iStartPos > 0
+        ? KErrNone
+        : KErrNotFound;
+
+    if ( IsActivated() && ( ret == KErrNone ) && IsFocused() )
+        {
+        TInt entryLength( 0 );  // not really needed
+        TInt entryStart( iAddressControlEditor->RichText()->CharPosOfParagraph(
+            entryLength, iParsingInfo.iEntryNum ) );
+        FormatAndSetCursorPosL( entryStart + iParsingInfo.iEndPos );
+        iAddressControlEditor->SetSelectionL(
+            entryStart + iParsingInfo.iEndPos,
+            entryStart + iParsingInfo.iStartPos );
+        }
+
+    return ret;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::SizeOfAddresses
+//
+// Returns size of addresses.
+// ---------------------------------------------------------
+//
+EXPORT_C void CMsgAddressControl::GetSizeOfAddresses(
+    TInt& aEntryCount, TInt& aSizeOfAddresses )
+    {
+    const CMsgAddressControlEditorField* field = NULL;
+    TBuf<KMaxEntryLength> entryBuf;
+    TInt ret( KErrNotFound );
+
+    aEntryCount = 0;
+    aSizeOfAddresses = 0;
+
+    for ( TInt entryNumber = iAddressControlEditor->EntryCount() - 1;
+         entryNumber >= 0;
+         entryNumber-- )
+        {
+        if ( iAddressControlEditor->IsEntryVerified( entryNumber ) )
+            {
+            field = iAddressControlEditor->Field( entryNumber );
+
+            aSizeOfAddresses += field->Address()->Length();
+            aEntryCount++;
+            }
+        else
+            {
+            SetParsingInfo( entryNumber, 0, 0 );
+            ret = KErrNone;
+            while ( ret == KErrNone )
+                {
+                ret = GetNextItemOnEntry( entryBuf );
+                if ( ( ret == KErrNone ) && ( entryBuf.Length() > 0 ) )
+                    {
+                    aSizeOfAddresses += entryBuf.Length();
+                    aEntryCount++;
+                    entryBuf.Zero();
+                    }
+                }
+            }
+        }
+
+    ResetParsingInfo();
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::HighlightUnvalidatedStringL
+//
+// Highlights the found unvvalidated entry.
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMsgAddressControl::HighlightUnvalidatedStringL()
+    {
+    ResetParsingInfo();
+
+    iAddressControlEditor->TurnHighlightingOffL();
+
+    TInt entryCount = iAddressControlEditor->EntryCount();
+    TInt entryNumber( iParsingInfo.iEntryNum );
+    TBuf<KMaxEntryLength> entryBuf;
+    TInt ret( KErrNotFound );
+
+    while ( entryNumber < entryCount )
+        {
+        entryBuf.Zero();
+        if ( iAddressControlEditor->IsEntryValidated( entryNumber ) )
+            {
+            entryNumber++;
+            SetParsingInfo( entryNumber, 0, 0 );
+            }
+        else
+            {
+            ret = GetNextItemOnEntry( entryBuf );
+            if ( ( ret == KErrNone ) && ( entryBuf.Length() > 0 ) )
+                {
+                TInt firstPos = iAddressControlEditor->EntryStartPos(
+                    iParsingInfo.iEntryNum );
+                FormatAndSetCursorPosL( firstPos + iParsingInfo.iEndPos );
+                break;
+                }
+            else if ( iParsingInfo.iEndPos > iParsingInfo.iStartPos )
+                {
+                // a string could not be found but there seems to be white
+                // spaces on an entry which may be also followed by the list
+                // separator, hence clean them up
+                iAddressControlEditor->DeleteEntryCharsL(
+                    iParsingInfo.iEntryNum,
+                    iParsingInfo.iStartPos,
+                    iParsingInfo.iEndPos );
+
+                SetParsingInfo(
+                    entryNumber,
+                    iParsingInfo.iStartPos,
+                    iParsingInfo.iStartPos );
+                }
+            else if ( ( iParsingInfo.iEndPos == 0 ) &&
+                ( iParsingInfo.iEntryNum < entryCount - 1 ) )
+                {
+                // there is just an EParagraphDelimiter on the entry
+                iAddressControlEditor->DeleteEntryL( iParsingInfo.iEntryNum );
+                SetParsingInfo( entryNumber, 0, 0 );
+                entryCount--;
+                }
+            else
+                {
+                // there are no strings left on the current entry
+                entryNumber++;
+                SetParsingInfo( entryNumber, 0, 0 );
+                }
+            }
+        }
+
+    if ( entryNumber >= entryCount )
+        {
+        FormatAndSetCursorPosL( iAddressControlEditor->TextLength() );
+        }
+
+    ret = iParsingInfo.iEndPos - iParsingInfo.iStartPos > 0
+        ? KErrNone
+        : KErrNotFound;
+
+    if ( IsActivated() && ( ret == KErrNone ) && IsFocused() )
+        {
+        TInt entryLength( 0 );  // not really needed
+        TInt entryStart( iAddressControlEditor->RichText()->CharPosOfParagraph(
+            entryLength, iParsingInfo.iEntryNum ) );
+        FormatAndSetCursorPosL( entryStart + iParsingInfo.iEndPos );
+        iAddressControlEditor->SetSelectionL(
+            entryStart + iParsingInfo.iEndPos,
+            entryStart + iParsingInfo.iStartPos );
+        }
+    return ret;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::IsPriorCharSemicolonL()
+//
+// Checks whether prior character is semicolon. Needed to variable
+// selection key functionality in editors.
+// ---------------------------------------------------------
+//
+EXPORT_C TBool CMsgAddressControl::IsPriorCharSemicolonL() const
+    {
+    TInt cursorPos = Editor().CursorPos();
+    HBufC* buf = Editor().GetTextInHBufL();
+    CleanupStack::PushL( buf );
+
+    TBool semicolonFound = EFalse;
+    TUint character;
+    while ( cursorPos > 1 && !semicolonFound )
+        {
+        cursorPos--;
+        character = TUint( (*buf)[cursorPos] );
+
+        if ( character == KMsgListSeparator || character == KArabicSemicolon )
+            {
+            semicolonFound = ETrue;
+            }
+        else if ( character != CEditableText::ESpace &&
+            character != CEditableText::EParagraphDelimiter )
+            {
+            break;
+            }
+        }
+    
+    CleanupStack::PopAndDestroy(); // buf
+    return semicolonFound;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::SetAddressFieldAutoHighlight()
+//
+// Turns on or off automatic recipient highlight on this address
+// field. Focus must stop to this control if highlight is enabled.
+// Updates also the highlight accordingly.
+// ---------------------------------------------------------
+//
+EXPORT_C void CMsgAddressControl::SetAddressFieldAutoHighlight( TBool aValidHighlightable )
+    {
+    iAddressControlEditor->SetAddressFieldAutoHighlight( aValidHighlightable );    
+    DrawDeferred();
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::AddressFieldAutoHighlight()
+//
+// ---------------------------------------------------------
+//
+EXPORT_C TBool CMsgAddressControl::AddressFieldAutoHighlight()
+    {
+    return iAddressControlEditor->AddressFieldAutoHighlight();
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::ConstructFromResourceL
+//
+// Creates this control from resource.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::ConstructFromResourceL( TInt aResourceId )
+    {
+    BaseConstructL();  // Sets margins only.
+
+    TResourceReader reader;
+    iCoeEnv->CreateResourceReaderLC( reader, aResourceId );
+
+    // Create caption.
+    iCaption = CreateCaptionFromResourceL( reader );
+    
+    // Read some information about control from resource.
+    ReadControlPropertiesFromResourceL( reader );
+
+    // Create editor.
+    iEditor = CreateEditorFromResourceL( reader );
+    
+    iEditor->SetControlType(EMsgAddressControl);
+    iEditor->SetObserver( this );
+    iEditor->SetEdwinSizeObserver( this );
+    iEditor->AddEdwinObserverL( this );
+    
+    SetPlainTextMode( ETrue );
+
+#ifdef RD_SCALABLE_UI_V2
+    if ( AknLayoutUtils::PenEnabled() )
+        {
+        CreateButtonL();
+        }
+#endif // RD_SCALABLE_UI_V2
+
+    ResolveLayoutsL();
+    
+    iControlModeFlags |= EMsgControlModeForceFocusStop;
+    
+    CleanupStack::PopAndDestroy();  // reader
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::NotifyViewEvent
+//
+// Builds up text fields for the control i.e. reads verified recipients and
+// creates corresponding text fields for them.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::NotifyViewEvent( TMsgViewEvent aEvent, TInt aParam )
+    {
+    if ( iAddressControlEditor->AddressFieldAutoHighlight() )
+        {
+        aParam |= EMsgViewEventAutoHighlight;
+        }
+
+    CMsgExpandableControl::NotifyViewEvent( aEvent, aParam );
+
+    if ( aEvent == EMsgViewEventPrepareForViewing )
+        {
+        if ( iAddressControlEditor->TextLength() && IsFocused() )
+            {
+            iAddressControlEditor->PrepareForViewing();
+            
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// ReadControlPropertiesFromResourceL
+//
+// Reads control properties from resource.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::ReadControlPropertiesFromResourceL(
+    TResourceReader& aReader )
+    {
+    CMsgExpandableControl::ReadControlPropertiesFromResourceL( aReader );
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::CreateEditorFromResourceL
+//
+// Creates editor (CEikRichTextEditor) for the control from resource and
+// returns pointer to it.
+// ---------------------------------------------------------
+//
+CMsgExpandableControlEditor* CMsgAddressControl::CreateEditorFromResourceL(
+    TResourceReader& aReader )
+    {
+    // Create an editor.
+    CMsgExpandableControlEditor* editor =
+        new ( ELeave ) CMsgAddressControlEditor(
+            this, iControlModeFlags, iBaseControlObserver );
+
+    CleanupStack::PushL( editor );
+    editor->ConstructFromResourceL( aReader );
+    CleanupStack::Pop();  // editor
+
+    return editor;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::EditPermission
+//
+// Checks and returns control's edit permissions. Edit permissions are needed
+// to check in order to know whether some key press is allowed to pass
+// to the control or not.
+// ---------------------------------------------------------
+//
+TUint32 CMsgAddressControl::EditPermission() const
+    {
+    return iAddressControlEditor->CheckEditPermission();
+    }
+
+
+// ---------------------------------------------------------
+// CMsgAddressControl::OfferKeyEventL
+//
+// Handles key events. The most of the keys are passed to the control editor.
+// ---------------------------------------------------------
+//
+TKeyResponse CMsgAddressControl::OfferKeyEventL(
+    const TKeyEvent& aKeyEvent, TEventCode aType )
+    {
+    // reset "trap" for extraneous multi-tap TextUpdate edwin events as we are
+    // now working on another input
+    iDuplicateEvent = EFalse;
+    
+    TKeyResponse keyResp = EKeyWasNotConsumed;
+    ResetParsingInfo();
+    TInt  cursorPos = iAddressControlEditor->CursorPos();
+
+    if ( aKeyEvent.iModifiers & EModifierShift )
+        {
+        iControlModeFlags |= EMsgControlModeShiftPressed;
+        }
+    else
+        {
+        iControlModeFlags &= ~EMsgControlModeShiftPressed;
+        }
+
+    switch ( aKeyEvent.iCode )
+        {
+        case EKeyHome:
+        case EKeyEnd:
+        case EKeyPageUp:
+        case EKeyPageDown:
+            {
+            break;
+            }
+        case EKeyUpArrow:
+        case EKeyDownArrow:
+            {
+            /*TInt fldpos = iAddressControlEditor->FirstFieldPos( cursorPos );
+            TInt entryNumber = iAddressControlEditor->EntryNumber( cursorPos );
+
+            if ( cursorPos != fldpos )
+                {
+                    iAddressControlEditor->MoveCursorToEntryPosL( entryNumber, 0 );
+                }*/
+            break;
+            }
+        case EKeyLeftArrow:
+            {
+            if ( iAddressControlEditor->IsPosAtEntryBeginning( cursorPos ) )
+                {
+                TInt entryNumber = iAddressControlEditor->EntryNumber( cursorPos );
+                if ( iAddressControlEditor->IsEntryVerified( entryNumber ) )
+                    {
+                    if ( iAddressControlEditor->IsEntryRightToLeft( entryNumber ) )
+                        {
+                        iAddressControlEditor->MoveCursorToEntryPosL(
+                            entryNumber,
+                            iAddressControlEditor->EntryLength( entryNumber ) );
+                        iAddressControlEditor->CheckHighlightingL();
+                        return EKeyWasConsumed;
+                        }
+                    }
+                }
+                
+            if ( !iAddressControlEditor->IsPosAtEntryBeginning( cursorPos ) )
+                {
+                TInt entryNumber = iAddressControlEditor->EntryNumber( cursorPos );
+                if (iAddressControlEditor->IsEntryVerified( entryNumber ) )
+                    {
+                    if ( !iAddressControlEditor->IsEntryRightToLeft( entryNumber ) )
+                        {
+                        iAddressControlEditor->MoveCursorToEntryPosL( entryNumber, 0 );
+                        iAddressControlEditor->CheckHighlightingL();
+                        return EKeyWasConsumed;
+                        }
+                    }
+                }
+                
+            break;
+            }
+        case EKeyRightArrow:
+            {
+            if ( iAddressControlEditor->IsPosAtEntryBeginning( cursorPos ) )
+                {
+                TInt entryNumber = iAddressControlEditor->EntryNumber( cursorPos );
+                if (iAddressControlEditor->IsEntryVerified( entryNumber ) )
+                    {
+                    if( !iAddressControlEditor->IsEntryRightToLeft( entryNumber ) )
+                        {
+                        iAddressControlEditor->MoveCursorToEntryPosL(
+                            entryNumber,
+                            iAddressControlEditor->EntryLength( entryNumber ) );
+                        iAddressControlEditor->CheckHighlightingL();
+                        return EKeyWasConsumed;
+                        }
+                    }
+                }
+            if ( !iAddressControlEditor->IsPosAtEntryBeginning( cursorPos ) )
+                {
+                TInt entryNumber = iAddressControlEditor->EntryNumber( cursorPos );
+                if (iAddressControlEditor->IsEntryVerified( entryNumber ) )
+                    {
+                    if( iAddressControlEditor->IsEntryRightToLeft( entryNumber ) )
+                        {
+                        iAddressControlEditor->MoveCursorToEntryPosL( entryNumber, 0 );
+                        iAddressControlEditor->CheckHighlightingL();
+                        return EKeyWasConsumed;
+                        }
+                    }
+                }
+            break;
+            }
+        case EKeyTab:
+        case EKeyInsert:
+            {
+            return EKeyWasConsumed;
+            }
+        case EKeyEnter:
+            {
+            TUint32 editPermission = iAddressControlEditor->CheckEditPermission();
+            if ( !( editPermission & EMsgEditParagraphDelimiter ) ||
+                ( aKeyEvent.iModifiers & EModifierShift ) )
+                {
+                return EKeyWasConsumed;
+                }
+                
+            iAddressControlEditor->TurnHighlightingOffL( iAddressControlEditor->Selection() );
+                
+            break;
+            }
+        case EKeyDelete:
+            {
+            TUint32 editPermission = iAddressControlEditor->CheckEditPermission();
+            if ( editPermission & EMsgEditRemoveEntry &&
+                 iAddressControlEditor->IsPosAtEntryBeginning( cursorPos ) )
+                {
+                RemoveHighlightedEntryL();
+                return EKeyWasConsumed;
+                }
+            else if ( !( editPermission & EMsgEditDelete ) )
+                {
+                return EKeyWasConsumed;
+                }
+                
+            iAddressControlEditor->TurnHighlightingOffL( iAddressControlEditor->Selection() );
+            break;
+            }
+        case EKeyBackspace:
+            {
+            TUint32 editPermission = iAddressControlEditor->CheckEditPermission();
+            
+            if ( editPermission & EMsgEditRemoveEntry )
+                {
+                RemoveHighlightedEntryL();
+                return EKeyWasConsumed;
+                }
+            else if ( editPermission & EMsgEditBackspaceMove )
+                {
+                TInt entryNumber = iAddressControlEditor->EntryNumber( cursorPos ) - 1;
+                iAddressControlEditor->MoveCursorToEntryPosL(
+                    entryNumber,
+                    iAddressControlEditor->EntryLength( entryNumber ) );
+                return EKeyWasConsumed;
+                }
+            else if ( !( editPermission & EMsgEditBackspace ) )
+                {
+                return EKeyWasConsumed;
+                }
+                
+            iAddressControlEditor->TurnHighlightingOffL( iAddressControlEditor->Selection() );
+            
+            // This is for disabling paragraph adding when removing line feed after 
+            // semicolon. OfferKeyEventL below will trigger EEventTextUpdate event.
+            iDuplicateEvent = ETrue;
+            
+            break;
+            }
+        default:
+            {
+            if ( !iAddressControlEditor->IsHandleEditEvent() )
+                {
+                TUint32 editPermission = iAddressControlEditor->CheckEditPermission();
+                if ( !( editPermission & EMsgEditCharInsert ) )
+                    {
+                    return EKeyWasConsumed;
+                    }
+                }
+            iAddressControlEditor->TurnHighlightingOffL( iAddressControlEditor->Selection() );
+            break;
+            }
+        }
+
+    keyResp = iAddressControlEditor->OfferKeyEventL( aKeyEvent, aType );
+
+    return keyResp;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::HandleEdwinEventL
+//
+//
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::HandleEdwinEventL(
+    CEikEdwin* aEdwin, TEdwinEvent aEventType )
+    {
+    CMsgExpandableControl::HandleEdwinEventL( aEdwin, aEventType );
+
+    if ( aEventType == MEikEdwinObserver::EEventNavigation )
+        {
+        if ( iAddressControlEditor->CursorPos() ==
+            iAddressControlEditor->TextLength() )
+            {
+            iAddressControlEditor->TurnHighlightingOffFromPosL( 0 );
+            }
+        iAddressControlEditor->CheckHighlightingL();
+        }
+    if ( aEventType == MEikEdwinObserver::EEventTextUpdate )
+        {
+        iAddressControlEditor->PostInsertEditorFormattingL( iDuplicateEvent );
+        
+        // Reset duplicate event flag.
+        iDuplicateEvent = EFalse;
+        }
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::PrepareForReadOnly
+//
+// Prepares read only or non read only state.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::PrepareForReadOnly( TBool aReadOnly )
+    {
+    CMsgExpandableControl::PrepareForReadOnly( aReadOnly );
+    
+#ifdef RD_SCALABLE_UI_V2
+    if ( !aReadOnly && 
+         AknLayoutUtils::PenEnabled() )
+        {
+        if ( !iButton )
+            {
+            TRAPD( error, CreateButtonL() );
+            if ( error == KErrNone )
+                {
+                LayoutButton();
+                }
+            else
+                {
+                delete iButton;
+                iButton = NULL;
+                }
+            }
+        }
+    else 
+        {
+        // No button is created at the read only state or when pen is disabled
+        delete iButton;
+        iButton = NULL;
+        }
+#endif
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::FocusChanged
+//
+// This is called when the focus of the control is changed.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::FocusChanged( TDrawNow aDrawNow )
+    {
+    CMsgExpandableControl::FocusChanged( aDrawNow );
+
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::HandleResourceChange
+//
+// This is called when the focus of the control is changed.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::HandleResourceChange( TInt aType )
+    {
+
+    CMsgExpandableControl::HandleResourceChange( aType );
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::GetNextItemOnEntry
+//
+// Finds the next unverified string on entry and updates parsing info
+// correspondingly. Returns ETrue if found.
+// ---------------------------------------------------------
+//
+TInt CMsgAddressControl::GetNextItemOnEntry( TDes& aEntryBuf )
+    {
+    TInt entryNumber = iParsingInfo.iEntryNum;
+    TInt startPos = iParsingInfo.iEndPos;
+    TInt endPos = iParsingInfo.iEndPos;
+
+    TPtrC entryString( iAddressControlEditor->ReadEntry( entryNumber ) );
+    TInt ret = ParseString( aEntryBuf, entryString, startPos, endPos );
+    SetParsingInfo( entryNumber, startPos, endPos );
+
+    return ret;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::ParseString
+//
+// Finds and returns an unverified string aEntryItem from aString starting at
+// position aStartPos. Returns a start position aStartPos and an end position
+// aEndPos of the searched range. Returns KErrNotFound if the string cannot be
+// found or KErrOverflow if a sting was too long.
+// ---------------------------------------------------------
+//
+TInt CMsgAddressControl::ParseString(
+    TDes& aEntryItem, const TPtrC& aString, TInt& aStartPos, TInt& aEndPos )
+    {
+    __ASSERT_DEBUG( aStartPos >= 0, Panic( EMsgInvalidStartPos ) );
+
+    aEntryItem.Zero();
+    aEndPos = aStartPos;
+    TInt stringLength = aString.Length();
+    TInt ret( KErrNone );
+
+    while ( aEndPos < stringLength )
+        {
+        // finds suitable strings e.g. kalle;^   kalle^   ______;^
+        if ( ( ( aString[aEndPos] == KMsgListSeparator ) ||
+            ( aString[aEndPos] == KArabicSemicolon ) ||
+            ( aString[aEndPos] == CEditableText::ELineBreak ) ||
+            ( aString[aEndPos] == KDownwardsArrowWithTipLeftwards ) ||
+            ( aString[aEndPos] == KDownwardsArrowWithTipRightwards ) )
+            // SJK 25.04.2005: Fixing "HNUN-6BPAVU":
+            /*&& ( aStartPos != aEndPos )*/ )
+            {
+            aEndPos++;
+            break;
+            }
+        else if ( ( ( aString[aEndPos] != KMsgListSeparator ) ||
+            ( aString[aEndPos] != KArabicSemicolon ) ) &&
+            ( aEntryItem.Length() < aEntryItem.MaxLength() ) )
+            {
+            aEntryItem.Append( aString[aEndPos] );
+            }
+        else if ( aString[aEndPos] != CEditableText::ESpace )
+            {
+            // Do we ever get here? Even in theory!?
+            // What is this branch for?!
+            aEndPos++;
+            break;
+            }
+        aEndPos++;
+        }
+
+    aEntryItem.TrimAll();
+
+    if ( ( aEntryItem.Length() == 0 ) && ( aStartPos == aEndPos ) )
+        {
+        ret = KErrNotFound;
+        }
+
+    return ret;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::FormatAndSetCursorPosL
+//
+// Formats and sets the cursor to the beginning of the formatted band.
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::FormatAndSetCursorPosL( TInt aPos )
+    {
+    if ( iControlModeFlags & EMsgControlModeInitialized )
+        {
+        iAddressControlEditor->SetAmountToFormatL( ETrue );
+        iAddressControlEditor->CheckHighlightingL();
+        }
+
+    // Formats and draws the content
+    iAddressControlEditor->SetCursorPosAndCancelSelectionL( aPos );
+    if ( iControlModeFlags & EMsgControlModeInitialized )
+        {
+        iAddressControlEditor->CheckHighlightingL();
+        iBaseControlObserver->HandleBaseControlEventRequestL(
+            this, EMsgEnsureCorrectFormPosition );
+        }
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::RemoveHightlightedEntryL
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::RemoveHighlightedEntryL( )
+    {
+    iAddressControlEditor->TurnHighlightingOffL();
+    TInt cursorPos( iAddressControlEditor->CursorPos() );
+    TInt entryNumber = iAddressControlEditor->EntryNumber( iAddressControlEditor->CursorPos() );
+    //Count the paragraph delimiter while capturing length
+    TInt entryLength = iAddressControlEditor->EntryLength( entryNumber )+1;
+    //Clear the entry
+    iAddressControlEditor->DeleteEntryCharsL( entryNumber, 0, entryLength );
+    iAddressControlEditor->DeleteEntryL( entryNumber );
+    FormatAndSetCursorPosL(iAddressControlEditor->CursorPos()); 
+
+    iControlModeFlags |= EMsgControlModeModified;
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::CreateButtonL
+// ---------------------------------------------------------
+//
+void CMsgAddressControl::CreateButtonL()
+    {
+#ifdef RD_SCALABLE_UI_V2
+    delete iButton;
+    iButton = NULL;
+    
+    iButton = CAknButton::NewL( NULL,
+                                NULL,
+                                NULL,
+                                NULL,
+                                *iCaption->Text(),
+                                KNullDesC,
+                                KAknButtonSizeFitText | KAknButtonTextLeft,
+                                0 );
+    
+    iButton->SetTextColorIds( KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG63 );
+    iButton->SetObserver( this );
+    
+    if ( Parent() )
+        {
+        iButton->SetContainerWindowL( *Parent() );
+        iButton->ActivateL();
+        }
+#endif // RD_SCALABLE_UI_V2
+    }
+
+// ---------------------------------------------------------
+// CMsgAddressControl::LayoutButton
+// ---------------------------------------------------------
+//    
+void CMsgAddressControl::LayoutButton()
+    {
+#ifdef RD_SCALABLE_UI_V2
+    CMsgExpandableControl::LayoutButton();
+    
+    if ( iButton )
+        {
+        TMargins8 margins;
+        TRect buttonRect = iButtonLayout.Rect();
+        TRect textRect = iCaptionLayout.TextRect();
+        margins.iTop = textRect.iTl.iY - buttonRect.iTl.iY;
+        margins.iBottom = buttonRect.iBr.iY - textRect.iBr.iY;
+        margins.iLeft = textRect.iTl.iX - buttonRect.iTl.iX;
+        margins.iRight = buttonRect.iBr.iX - textRect.iBr.iX;
+        
+        iButton->SetMargins( margins );
+        }
+#endif // RD_SCALABLE_UI_V2
+    }
+
+//  End of File