diff -r 000000000000 -r 72b543305e3a messagingappbase/msgeditor/viewsrc/MsgAddressControlEditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingappbase/msgeditor/viewsrc/MsgAddressControlEditor.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,1980 @@ +/* +* Copyright (c) 2002 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: MsgAddressControlEditor implementation +* +*/ + + + +// ========== INCLUDE FILES ================================ + +#include // for TResourceReader +#include // for CEikonEnv +#include // for CRichText +#include // for AknUtils +#include // for SettingCache().InputLanguage() +#include // for AknsUtils::GetCachedColor() +#include // for KAknsIIDQsnHighlightColors and EAknsCIQsnHighlightColorsCG1 + +#include // LAF +#include +#include + +#ifdef RD_TACTILE_FEEDBACK +#include +#endif + + +#include "MsgEditorCommon.h" // +#include "MsgAddressControlEditor.h" // for CMsgAddressControlEditor +#include "MsgAddressControlEditorField.h" // for CMsgAddressControlEditorField and TMsgAddressControlEditorFieldFactory +#include "MsgBaseControl.h" // for TMsgEditPermissionFlags +#include "MsgBaseControlObserver.h" // for MMsgBaseControlObserver +#include "MsgAddressControl.h" // for permission flags +#include "MsgEditorPanic.h" // for MsgEditor panics +#include "MsgEditor.hrh" // for EditPermission flags +#include "MsgEditorKeyCatcher.h" // for CMsgEditorKeyCatcher + +// ========== EXTERNAL DATA STRUCTURES ===================== + +// ========== EXTERNAL FUNCTION PROTOTYPES ================= + +// ========== CONSTANTS ==================================== + +// ========== MACROS ======================================= + +// ========== LOCAL CONSTANTS AND MACROS =================== + +// Highlighted field does not exist. +const TInt KMsgHighlightedFieldPosNone = -1; +// Current entry not defined. +const TInt KMsgCurrentEntryNone = -1; + +const TText KZeroWidthJoiner = 0x200D; +const TText KLRMarker = 0x200E; +const TText KRLMarker = 0x200F; + +// ========== MODULE DATA STRUCTURES ======================= + +// ========== LOCAL FUNCTION PROTOTYPES ==================== + +// ========== LOCAL FUNCTIONS ============================== + +// ========== MEMBER FUNCTIONS ============================= + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CMsgAddressControlEditor +// +// Constructor. +// --------------------------------------------------------- +// +CMsgAddressControlEditor::CMsgAddressControlEditor( + const CCoeControl* aParent, + TUint32& aFlags, + MMsgBaseControlObserver* aBaseControlObserver ) + : CMsgExpandableControlEditor( aParent, aFlags, aBaseControlObserver ), + iHighLightedFieldPos( KMsgHighlightedFieldPosNone ), + iCurrentEntry( KMsgCurrentEntryNone ) + { + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::~CMsgAddressControlEditor +// +// Destructor. +// --------------------------------------------------------- +// +CMsgAddressControlEditor::~CMsgAddressControlEditor() + { + delete iKeyCatcher; + delete iAddressControlEditorFieldFactory; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::ConstructL +// +// 2nd phase constructor. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::ConstructL() + { + CMsgExpandableControlEditor::ConstructL(); + + //To Dis-able Smiley support for address fields + RemoveFlagFromUserFlags(EAvkonEnableSmileySupport); + + iFlags |= EMsgControlModeDoNotDrawFrameBgContext; + iKeyCatcher = new ( ELeave ) CMsgEditorKeyCatcher(); + iKeyCatcher->ConstructL( this ); + + SetAllowUndo( EFalse ); + // Variation is done inside edwin + SetPhoneNumberGrouping( ETrue ); + iCusPos = 0; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::ExtractEntry +// +// Extracts an entry according to aEntryNumber and returns it in aEntryString. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::ExtractEntry( + TDes& aEntryString, TInt aEntryNumber ) + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + iRichText->Extract( aEntryString, entryStart, entryLength - 1 ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::ReadEntry +// +// Reads an entry aEntryNumber and returns pointer to it. +// --------------------------------------------------------- +// +TPtrC CMsgAddressControlEditor::ReadEntry( TInt aEntryNumber ) const + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + return iRichText->Read( entryStart, entryLength - 1 ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::EntryCount +// +// Returns number of entries. +// --------------------------------------------------------- +// +TInt CMsgAddressControlEditor::EntryCount() const + { + return iRichText->ParagraphCount(); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::DeleteEntryL +// +// Deletes an entry aEntryNumber. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::DeleteEntryL( TInt aEntryNumber ) + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + CancelFepTransaction(); + TBool setcursorpos(EFalse); + TInt numOfEntries = iRichText->ParagraphCount(); + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + if ( numOfEntries == aEntryNumber + 1 ) //entry to be deleted is the lowest one + { + if ( aEntryNumber != 0 ) // but not the only one + { + entryStart--; + } + else + { + entryLength--; + } + } + + if ( TextView() ) + { + // Cursor position must not go beyond text end. Otherwise + // panic will happen on text formatting. + TInt newTextLength = TextLength() - entryLength; + + if ( CursorPos() > newTextLength ) + { + SetCursorPosL( newTextLength, EFalse ); + iCusPos = newTextLength; + setcursorpos = ETrue; + } + } + + iRichText->CancelInsertCharFormat(); + iRichText->DelSetInsertCharFormatL( entryStart, entryLength ); + if ( iTextView ) + { + iTextView->HandleInsertDeleteL( + TCursorSelection( entryStart, entryStart + entryLength ), entryLength, EFalse ); + } + numOfEntries = iRichText->ParagraphCount(); + if( numOfEntries > 0 && numOfEntries != aEntryNumber && !setcursorpos) + { + TInt txtlen = TextLength(); + TInt curpos = CursorPos(); + entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + curpos+= --entryLength; + if ( TextView() ) + { + if(curpos < txtlen) + { + iCusPos = curpos; + SetCursorPosL( curpos, EFalse ); + } + else + { + iCusPos =entryStart ; + SetCursorPosL( iCusPos, EFalse ); + ReportEdwinEventL( MEikEdwinObserver::EEventTextUpdate ); + } + } + } + } + // --------------------------------------------------------- +// CMsgAddressControlEditor::GetCursorpos +// +// returns the current cursor position. +// --------------------------------------------------------- +// + +TInt CMsgAddressControlEditor::GetCursorpos() const + { + return iCusPos; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::ClearEntryL +// +// Clears all the text of an entry aEntryNumber but not the paragraph delimiter. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::ClearEntryL( TInt aEntryNumber, TBool aUpdate ) + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + DeleteEntryCharsL( aEntryNumber, 0, entryLength ); + if ( iTextView && aUpdate ) + { + iTextView->HandleInsertDeleteL( + TCursorSelection( entryStart, entryStart ), --entryLength, EFalse ); + SetCursorPosAndCancelSelectionL( entryStart ); + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::DeleteAllL +// +// Clears the content of the editor. Updates also the scroll bars. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::DeleteAllL() + { + CancelFepTransaction(); + iRichText->CancelInsertCharFormat(); + iRichText->Reset(); + if (iTextView) + { + SetAmountToFormatL( ETrue ); + SetCursorPosAndCancelSelectionL( 0 ); + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::DeleteEntryCharsL +// +// Deletes characters from aStartPos to aEndPos at entry aEntryNumber. Never +// removes the paragraph delimiter. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::DeleteEntryCharsL( + TInt aEntryNumber, TInt aStartPos, TInt aEndPos) + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + __ASSERT_DEBUG( ( aStartPos >= 0 ) && ( aStartPos <= aEndPos ), + Panic( EMsgInvalidStartPos ) ); + + CancelFepTransaction(); + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + entryLength--; + entryLength = ( aEndPos < entryLength ) + ? aEndPos - aStartPos + : entryLength - aStartPos; + entryStart += aStartPos; + + if ( TextView() ) + { + // Cursor position must not go beyond text end. Otherwise + // panic will happen on text formatting. + TInt newTextLength = TextLength() - entryLength; + + if ( CursorPos() > newTextLength ) + { + SetCursorPosL( newTextLength, EFalse ); + } + } + + iRichText->CancelInsertCharFormat(); + iRichText->DelSetInsertCharFormatL( entryStart, entryLength ); + //clear the iTextView cache too + if ( iTextView ) + { + iTextView->HandleInsertDeleteL( + TCursorSelection( entryStart, entryStart + entryLength ), entryLength, EFalse ); + SetCursorPosAndCancelSelectionL( entryStart ); + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::SetCursorPosAndCancelSelectionL +// +// Sets cursor position to aPos and cancels selection. +// This is needed because Edwin/TextView does not do it as expected. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::SetCursorPosAndCancelSelectionL( TInt aPos ) + { + if ( iTextView ) + { + CancelFepTransaction(); + + iTextView->SetDocPosL( aPos, EFalse ); + ForceScrollBarUpdateL(); + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::InsertEntryL +// +// Inserts an entry to the location aEntryNumber with strings a text aName +// and aAddress. Makes an entry as verified if aVerified = ETrue and validated +// if aValidated = ETrue. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::InsertEntryL( + TInt aEntryNumber, + const TDesC& aName, + const TDesC& aAddress, + TBool aVerified, + TBool aValidated, + const MVPbkContactLink* aContactLink ) + { + __ASSERT_DEBUG( aEntryNumber >= 0 && + aEntryNumber <= iRichText->ParagraphCount(), + Panic( EMsgInvalidEntryNumber ) ); + + TChar semicolon = KSemicolon; + TInt entryLength = 0; + TInt numOfEntries = iRichText->ParagraphCount(); + + TInt pos = TextLength(); + TChar ch = CharAtPos( pos - 1 ); + + if ( ch == CEditableText::EParagraphDelimiter ) + { + // last character is parag.delim. => decrease entry num. + aEntryNumber--; + } + + /* + * Fix the split contact name issue::ECYU-7FPC93. + * Replace any enter-key characters with space characters. + */ + + TBufC parsedName(aName.Left(KMaxContactLength)); + + TInt enterKeyPos; + TChar enterChar( CEditableText::EParagraphDelimiter ); + TChar spaceChar( CEditableText::ESpace ); + TPtr namePtr = parsedName.Des(); + TInt nameLength = parsedName.Length(); + + enterKeyPos = namePtr.Locate(enterChar); + while( enterKeyPos != KErrNotFound && enterKeyPos < nameLength ) + { + namePtr[enterKeyPos] = spaceChar; + enterKeyPos = namePtr.Locate(enterChar); + } + + if ( aEntryNumber >= numOfEntries ) + { + // entry appended to the end. + iRichText->CancelInsertCharFormat(); + iRichText->InsertL( pos, CEditableText::EParagraphDelimiter ); + } + else + { + // entry appended to the middle or beginning. + iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + + if ( entryLength - 1 ) + { + InsertEntryBreakL( aEntryNumber, 0 ); + } + } + + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + + //ECYU-7FPC93:: Fix + HBufC* string = CreateEntryStringLC( parsedName, aAddress, aVerified ); + //HBufC* string = CreateEntryStringLC( aName, aAddress, aVerified ); + + if ( aVerified || aValidated ) + { + // Setting up the field factory. + iRichText->SetFieldFactory( iAddressControlEditorFieldFactory ); + + CMsgAddressControlEditorField* field = + static_cast( + iRichText->NewTextFieldL( KMsgAddressControlEditorFieldUid ) ); + + if ( field ) + { + field->SetEntryStringL( *string ); + + //ECYU-7FPC93:: Fix + field->SetNameL( parsedName ); + //field->SetNameL( aName ); + + field->SetAddressL( aAddress ); + field->SetVerified( aVerified ); + field->SetValidated( aValidated ); + field->SetContactLink( aContactLink ); + + iRichText->InsertFieldL( entryStart, + field, + KMsgAddressControlEditorFieldUid ); + + iRichText->UpdateFieldL( entryStart ); + } + } + else + { + if ( string->Length() > 0 ) + { + iRichText->InsertL( entryStart, *string ); + } + else + { + // This should not happen. Zero lenght contact name is being added. + // Insert semicolon only. + if ( iAvkonEnv->SettingCache().InputLanguage() == ELangArabic ) + { + semicolon = KArabicSemicolon; + } + + iRichText->InsertL( entryStart, semicolon ); + } + } + + if ( aEntryNumber >= numOfEntries ) + { + if ( ch != semicolon && + ch != CEditableText::EParagraphDelimiter && + ch != KArabicSemicolon ) + { + if ( iAvkonEnv->SettingCache().InputLanguage() == ELangArabic ) + { + semicolon = KArabicSemicolon; + } + + iRichText->InsertL( pos, semicolon ); + } + } + + CleanupStack::PopAndDestroy(); // string + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::InsertEntryBreakL +// +// Inserts the paragraph delimiter at aPos on entry aEntryNumber. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::InsertEntryBreakL( TInt aEntryNumber, TInt aPos ) + { + __ASSERT_DEBUG( aEntryNumber >= 0 && + aEntryNumber < iRichText->ParagraphCount(), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + TInt insertPos = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + insertPos += aPos; + iRichText->CancelInsertCharFormat(); + iRichText->InsertL( insertPos, CEditableText::EParagraphDelimiter ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::EntryNumber +// +// Returs an entry number where aPos is on. +// --------------------------------------------------------- +// +TInt CMsgAddressControlEditor::EntryNumber( TInt aPos ) const + { + __ASSERT_DEBUG( ( aPos >= 0 ) && ( aPos <= TextLength() ), + Panic( EMsgDocPosOutOfRange ) ); + + // first paragraph==0 + TInt paragraphNumber = iRichText->ParagraphNumberForPos( aPos ); + return paragraphNumber; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::EntryLength +// +// Returns length of the entry aEntryNumber. Does not count paragraph delimiter. +// --------------------------------------------------------- +// +TInt CMsgAddressControlEditor::EntryLength( TInt aEntryNumber ) const + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + return entryLength - 1; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::EntryStartPos +// +// Returns first character position on the entry aEntryNumber. +// --------------------------------------------------------- +// +TInt CMsgAddressControlEditor::EntryStartPos( TInt aEntryNumber ) const + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + return iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::FirstFieldPos +// +// +// --------------------------------------------------------- +// +TInt CMsgAddressControlEditor::FirstFieldPos( TInt aCursorPos ) const + { + TFindFieldInfo fieldInfo; + + /*TBool ret =*/ iRichText->FindFields( fieldInfo, aCursorPos, 0 ); + + return fieldInfo.iFirstFieldPos; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::MoveCursorToEntryPosL +// +// Moves the cursor on an entry aEntryNumber to a position aPos. 0 == entry start. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::MoveCursorToEntryPosL( TInt aEntryNumber, TInt aPos ) + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + if ( entryLength - 1 < aPos ) + { + aPos = entryLength - 1; // adjust the aPos if it happens to be too big + } + SetCursorPosAndCancelSelectionL( entryStart + aPos ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsPosAtEntryBeginning +// +// Checks whether the cursor is at the beginning of the entry and returns ETrue +// if it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsPosAtEntryBeginning( TInt aPos ) const + { + __ASSERT_DEBUG( ( aPos >= 0 ) && ( aPos <= TextLength() ), + Panic( EMsgDocPosOutOfRange ) ); + + TInt entryLength = 0; + return ( iRichText->CharPosOfParagraph( + entryLength, EntryNumber( aPos ) ) == aPos ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsEntryVerified +// +// Check whether an entry aEntryNumber is verified or not and returns ETrue +// if it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsEntryVerified( TInt aEntryNumber ) const + { + const CMsgAddressControlEditorField* field = Field( aEntryNumber ); + + return ( field ? field->IsVerified() : EFalse ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsPriorEntryVerified +// +// Checks whether an entry aEntryNumber-1 is verified and returns ETrue if +// it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsPriorEntryVerified( TInt aEntryNumber ) const + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + if ( aEntryNumber > 0 ) + { + return IsEntryVerified( aEntryNumber - 1 ); + } + + return EFalse; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsEntryValidated +// +// Check whether an entry aEntryNumber is validated or not and returns ETrue +// if it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsEntryValidated( TInt aEntryNumber ) const + { + const CMsgAddressControlEditorField* field = Field( aEntryNumber ); + + return ( field ? field->IsValidated() : EFalse ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsFieldOnLeft +// +// Checks if there is a text field on the left hand side of the cursor at +// position aPos and returns ETrue if it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsFieldOnLeft( TInt aPos ) const + { + if ( aPos > 0 && aPos <= TextLength() ) + { + const CMsgAddressControlEditorField* field = FieldFromPos( aPos - 1 ); + + return ( field ? field->IsVerified() : EFalse ); + } + return EFalse; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsFieldOnRight +// +// Checks if there is a text field on the right hand side of the cursor at position +// aPos and returns ETrue if it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsFieldOnRight( TInt aPos ) const + { + if ( aPos >= 0 && aPos < TextLength() ) + { + // Fixing Fixing "ESPI-63C45M - Messags: The contact named with one + // characteronly cannot be deleted from ‘To’ field by ‘Clear’ key." + + const CMsgAddressControlEditorField* field = FieldFromPos( aPos ); + + return ( field ? field->IsVerified() : EFalse ); + } + return EFalse; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsFieldOnPos +// +// Checks if there is a text field under the cursor at position +// aPos and returns ETrue if it is, otherwise EFalse. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsFieldOnPos( TInt aPos ) const + { + if ( aPos >= 0 && aPos < TextLength() ) + { + const CMsgAddressControlEditorField* field = FieldFromPos( aPos ); + + return ( field ? field->IsVerified() : EFalse ); + } + return EFalse; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsEntryEmpty +// +// Checks whether an entry aEntryNumber is empty. +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsEntryEmpty( TInt aEntryNumber ) const + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + + return ( entryLength == 1 ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::IsEntryRightToLeft +// +// --------------------------------------------------------- +// +TBool CMsgAddressControlEditor::IsEntryRightToLeft( TInt aEntryNumber ) const + { + TBool isRightToLeft( EFalse ); + + const CMsgAddressControlEditorField* field = Field( aEntryNumber ); + if ( field ) + { + TPtrC entryText = ReadEntry( aEntryNumber ); + if ( entryText.Length() > 0 ) + { + if ( TBidiText::TextDirectionality( entryText ) == + TBidiText::ERightToLeft ) + { + isRightToLeft = ETrue; + } + } + } + return isRightToLeft; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CheckHighlightingL +// +// Turns entry as highlighted if the cursor is on the top of the verified +// entry, otherwise turns highlighting off if there were any somewhere else. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::CheckHighlightingL( TBool aCancelFep /*= ETrue*/ ) + { + if ( !iTextView ) + { + return ; + } + + if ( aCancelFep && IsFocused()) + { + CancelFepTransaction(); + } + + TInt cursorPos = CursorPos(); + + if ( !IsReadOnly() && IsFieldOnPos( cursorPos ) ) + { + TFindFieldInfo fieldInfo; + + if ( iRichText->FindFields( fieldInfo, cursorPos, 0 ) ) + { + TCharFormat charFormat; + TCharFormatMask applyMask; + + if ( iHighLightedFieldPos != fieldInfo.iFirstFieldPos ) + { + if ( !( iFlags & EMsgControlModeShiftPressed ) ) + { + TRgb highlightColor; + + AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), + highlightColor, + KAknsIIDQsnHighlightColors, + EAknsCIQsnHighlightColorsCG1 ); + + charFormat.iFontPresentation.iHighlightColor = highlightColor; + applyMask.SetAttrib( EAttFontHighlightColor ); + + charFormat.iFontPresentation.iHighlightStyle = + TFontPresentation::EFontHighlightNormal; + applyMask.SetAttrib( EAttFontHighlightStyle ); + + // Sets only the attributes specified to be set on mask. + iRichText->ApplyCharFormatL( + charFormat, + applyMask, + fieldInfo.iFirstFieldPos, + fieldInfo.iFirstFieldLen ); + } + + if ( iTextView ) + { + iTextView->HandleRangeFormatChangeL( + TCursorSelection( + fieldInfo.iFirstFieldPos, + fieldInfo.iFirstFieldPos + fieldInfo.iFirstFieldLen ), + EFalse ); + } + + if ( cursorPos != fieldInfo.iFirstFieldPos && + !iHandlingPointerEvent ) + { + MoveCursorToEntryPosL(EntryNumber( cursorPos ), 0 ); + } + + TurnHighlightingOffL( aCancelFep ); + iHighLightedFieldPos = fieldInfo.iFirstFieldPos; + } + } + } + else + { + if ( IsReadOnly() && iValidHighlightable ) + { + // Use "link highlight" + SetHighlightStyleL( EEikEdwinHighlightLink ); + SelectAllL(); + } + else + { + TurnHighlightingOffL( aCancelFep ); + if ( IsReadOnly() ) + { + iTextView->SetDocPosL( cursorPos, ETrue ); + } + } + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::TurnHighlightingOffL +// +// Turns highlighting off if it happens to be on aSelection. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::TurnHighlightingOffL( TCursorSelection aSelection ) + { + if ( !Selection().Length() || + ( iHighLightedFieldPos == KMsgHighlightedFieldPosNone ) ) + { + return; + } + + if ( IsFieldOnPos( iHighLightedFieldPos ) ) + { + CancelFepTransaction(); + + TInt selLowerPos = aSelection.LowerPos(); + // The higherPos is more close to the end of the text + TInt selHigherPos = aSelection.HigherPos(); + + TFindFieldInfo fieldInfo; + iRichText->FindFields( fieldInfo, iHighLightedFieldPos, 0 ); + + if ( ( selLowerPos >= iHighLightedFieldPos ) && + ( iHighLightedFieldPos + fieldInfo.iFirstFieldLen <= selHigherPos ) ) + { + TurnHighlightingOffL(); + } + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::TurnHighlightingOffL +// +// Turns highlighting off if such a highlighted text field is somewhere. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::TurnHighlightingOffL( TBool aCancelFep /*= ETrue*/ ) + { + TInt textLength = TextLength(); + + if ( ( iHighLightedFieldPos == KMsgHighlightedFieldPosNone ) || + ( iHighLightedFieldPos > textLength ) ) + { + return; + } + + if ( aCancelFep ) + { + CancelFepTransaction(); + } + + TInt cursorPos = CursorPos(); + + if ( cursorPos == textLength ) + { + if ( IsFieldOnPos( cursorPos - 1 ) ) + { + // Remove the highlight from last cursor position only if + // there is highlightable field. + TurnHighlightingOffFromPosL( cursorPos - 1 ); + } + } + + TurnHighlightingOffFromPosL( iHighLightedFieldPos ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::TurnHighlightingOffFromPosL +// +// Turns highlighting off from position aPos. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::TurnHighlightingOffFromPosL( TInt aPos ) + { + TInt textLength = TextLength(); + + if ( iHighLightedFieldPos == KMsgHighlightedFieldPosNone || + aPos < 0 || + aPos > textLength ) + { + return; + } + + TFindFieldInfo fieldInfo; + + if ( !iRichText->FindFields( fieldInfo, aPos, 0 ) ) + { + // iHighLightedFieldPos is not updated correctly and hence, clear whole text + fieldInfo.iFirstFieldPos = 0; + fieldInfo.iFirstFieldLen = textLength; + __ASSERT_DEBUG( EFalse, Panic( EMsgInvalidStartPos ) ); + } + + TCharFormat charFormat; + TCharFormatMask applyMask; + + // background color. + charFormat.iFontPresentation.iHighlightColor = AKN_LAF_COLOR( 0 ); + applyMask.SetAttrib( EAttFontHighlightColor ); + + charFormat.iFontPresentation.iHighlightStyle = TFontPresentation::EFontHighlightNone; + applyMask.SetAttrib( EAttFontHighlightStyle ); + + // Sets only the attributes specified to be set on mask. + iRichText->ApplyCharFormatL( charFormat, + applyMask, + fieldInfo.iFirstFieldPos, + fieldInfo.iFirstFieldLen ); + + if ( iTextView ) + { + TCursorSelection selection( fieldInfo.iFirstFieldPos, + fieldInfo.iFirstFieldPos + fieldInfo.iFirstFieldLen ); + iTextView->HandleRangeFormatChangeL( selection, EFalse ); + } + + if ( iHighLightedFieldPos == aPos ) + { + // If highlight was removed from currently highlighted position + // reset highlighted field position. + iHighLightedFieldPos = KMsgHighlightedFieldPosNone; + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CreateEntryStringLC +// +// Creates a suitable text string for the text field using strings aName and +// aAddress and return pointer to it. If aFormat == EFalse, this function does +// not format the string but takes only aAddess. If text string does not fit to +// view it clips it and adds ... to end of it. If text layout does not exist +// this function does not attempt to clip text string. +// --------------------------------------------------------- +// + +HBufC* CMsgAddressControlEditor::CreateEntryStringLC( + const TDesC& aName, + const TDesC& aAddress, + TBool aVerified /*= ETrue*/ ) + { + HBufC* addressCopy = aAddress.AllocLC(); + TPtr addrTPtr = addressCopy->Des(); + AknTextUtils::DisplayTextLanguageSpecificNumberConversion( addrTPtr ); + + if ( aVerified || IsReadOnly() ) + { + TInt len = aName.Length(); + if ( len == 0 ) + { + len = addrTPtr.Length(); + } + + // +2 for KZeroWidthJoiner and direction marker + HBufC* string = HBufC::NewLC( len + 2 ); + TPtr strPtr = string->Des(); + + if ( aName.Length() == 0 ) + { + strPtr.Append( addrTPtr ); + } + else + { + strPtr.Append( aName ); + } + + TInt maxWidth = 0; + + if ( iTextView ) + { + // We must have iTextView because LayoutWidth calls CursorPos. + maxWidth = LayoutWidth(); + } + + if ( maxWidth > 0 ) + { + const CFont* font = TextFont(); + + if ( strPtr.Length() ) + { + // Clip a little bit more than maxWidth to prevent wrapping. + TInt clipWidth = Max( 0, maxWidth - 4 ); + TInt ellipsisWidth = font->CharWidthInPixels( KEllipsis ); + + TInt fits = font->TextCount( strPtr, clipWidth ); + + if ( fits < strPtr.Length() ) + { + fits = font->TextCount( strPtr, clipWidth - ellipsisWidth ); + + // Work out the text directionality before the truncation point. + // First put the text in reverse order and then call + // TBidiText::TextDirectionality. This effectively tells the + // directionality of the last strongly directional character + // in the text. + HBufC* temp = strPtr.Left( fits ).AllocL(); + TPtr tempPtr = temp->Des(); + + TInt i = 0; + TInt j = tempPtr.Length() - 1; + while ( i < j ) + { + TText t = tempPtr[i]; + tempPtr[i++] = tempPtr[j]; + tempPtr[j--] = t; + } + + TBidiText::TDirectionality dir = + TBidiText::TextDirectionality( tempPtr ); + + delete temp; + + TText dirMarker = ( dir == TBidiText::ERightToLeft ) + ? KRLMarker + : KLRMarker; + + // Insert zero width joiner if necessary to get the correct glyph form + // before truncation. + + TText lastChar = strPtr[fits - 1]; + TText next = strPtr[fits]; + + TInt ellipsisPos = fits; + + if ( CFont::CharactersJoin( lastChar, KZeroWidthJoiner ) && + CFont::CharactersJoin( lastChar, next ) ) + { + strPtr[fits] = KZeroWidthJoiner; + ellipsisPos++; + } + strPtr.SetMax(); + strPtr[ellipsisPos] = KEllipsis; + // Inserting direction marker after the ellipsis ensures that + // the ellipsis is shown on the correct side of the text + // before it. + strPtr[ellipsisPos + 1] = dirMarker; + + strPtr.SetLength( ellipsisPos + 2 ); + } + } + } + + HBufC* newString = strPtr.AllocL(); + CleanupStack::PopAndDestroy( 2 ); // string, addressCopy + + CleanupStack::PushL(newString); + return newString; + } + else + { + HBufC* string = addrTPtr.AllocL(); + CleanupStack::PopAndDestroy(); // addressCopy + CleanupStack::PushL( string ); + return string; + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::ReBuildEntryStringsL +// +// Re-builds all the text strings. Goes through all the fields in the +// text and assigns a new string into them. After this we call UpdateAllFieldsL +// to update all the fields and after this restores to cursor position +// to old position if it is valid or to the end of the text. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::ReBuildEntryStringsL() + { + const TInt textLength = TextLength(); + TInt length = textLength; + TInt docPos = 0; + TFindFieldInfo fieldInfo; + + iCurrentEntry = EntryNumber( CursorPos() ); + + while ( length > 0 && + docPos < textLength && + iRichText->FindFields( fieldInfo, docPos, length ) ) + { + CMsgAddressControlEditorField* field = + const_cast( FieldFromPos( fieldInfo.iFirstFieldPos ) ); + + HBufC* string = CreateEntryStringLC( *field->Name(), + *field->Address(), + field->IsVerified() ); + + field->SetEntryStringL( *string ); + + CleanupStack::PopAndDestroy( string ); + + if ( fieldInfo.iFieldCountInRange == 0 ) // no more fields left within a range + { + break; + } + + docPos = fieldInfo.iFirstFieldPos + fieldInfo.iFirstFieldLen; + length = textLength - docPos; + } + + if ( IsFocused() ) + { + CancelFepTransaction(); + } + + TInt oldCursorPos( CursorPos() ); + + // Sets cursor pos always to beginning + UpdateAllFieldsL(); + + oldCursorPos = Min( oldCursorPos, TextLength() ); + + // Restore the original cursor position if it was + // not out of the limit. + SetCursorPosL( oldCursorPos, EFalse ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::Field +// +// Returns a field of the entry aEntryNumber. If there is no field on that entry, +// returns NULL. +// --------------------------------------------------------- +// +const CMsgAddressControlEditorField* CMsgAddressControlEditor::Field( + TInt aEntryNumber ) const + { + __ASSERT_DEBUG( ( aEntryNumber >= 0 ) && + ( aEntryNumber < iRichText->ParagraphCount() ), + Panic( EMsgInvalidEntryNumber ) ); + + TInt entryLength = 0; + TInt entryStart = iRichText->CharPosOfParagraph( entryLength, aEntryNumber ); + + return FieldFromPos( entryStart ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CharAtPos +// +// +// --------------------------------------------------------- +// +TChar CMsgAddressControlEditor::CharAtPos( TInt aPos ) const + { + if ( aPos >= 0 && aPos < TextLength() ) + { + TBuf<1> buf; + iText->Extract( buf, aPos, 1 ); + return buf[0]; + } + else + { + return 0; + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::PrepareForViewing +// +// Prepares control for viewing. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::PrepareForViewing() + { + if ( iCurrentEntry >= 0 ) + { + TRAP_IGNORE( MoveCursorToEntryPosL( + iCurrentEntry, EntryLength( iCurrentEntry ) ) ); + + iCurrentEntry = KMsgCurrentEntryNone; + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::PreInsertEditorFormattingL +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::PreInsertEditorFormattingL( const TKeyEvent& aKeyEvent ) + { + if ( IsFocused() && !IsReadOnly() ) + { + TBool modified = EFalse; + TInt editPos = CursorPos(); + + // No preinserting is done in a case edit position is over + // text length (should not be possible) or if delete key is pressed. + if ( editPos > TextLength() || + aKeyEvent.iCode == EKeyDelete ) + { + return; + } + + TInt entryNumber = EntryNumber( editPos ); + TInt startPos = FirstFieldPos( editPos ); + TInt endPos = startPos + EntryLength( entryNumber ); + + if ( startPos < editPos && editPos < endPos && + IsEntryVerified( entryNumber ) ) + { + // Cursor is in the middle of verified entry, se inserting not possible. + return; + } + + TChar currentChar = CharAtPos( editPos ); + TChar previousChar = CharAtPos( editPos - 1 ); + + if ( IsFieldOnLeft( editPos + 1 ) ) // looking on the right, but before the key is inserted + { + // user is trying to add character to left of field: + // insert paragraph delimiter in front of the field, + // and in front of that a semicolon. + iTextView->SetDocPosL( editPos, EFalse ); + iRichText->CancelInsertCharFormat(); + InsertParagraphL( editPos ); + InsertSemicolonL( editPos ); + modified = ETrue; + } + else if ( IsFieldOnLeft( editPos ) ) + { + // user is trying to add character to right of field: + // insert semicolon after the field if there isn't one already, + // and after that a paragraph delimiter. + iTextView->SetDocPosL( editPos, EFalse ); + iRichText->CancelInsertCharFormat(); + if ( currentChar != KSemicolon && currentChar != KArabicSemicolon ) + { + InsertSemicolonL( editPos ); + } + InsertParagraphL( ++editPos ); + editPos++; + modified = ETrue; + } + else if ( ( previousChar == KSemicolon || + previousChar == KArabicSemicolon ) && + IsFieldOnLeft( editPos - 1 ) ) + { + // user is trying to add character to right of semicolon + // and there is field right next to it: + // insert a paragraph delimiter after semicolon. + iTextView->SetDocPosL( editPos, EFalse ); + iRichText->CancelInsertCharFormat(); + InsertParagraphL( editPos ); + editPos++; + modified = ETrue; + } + + if ( modified ) + { + CheckHighlightingL( EFalse ); + + TInt len = TextLength(); + if ( editPos > len ) + { + editPos = len; + } + + iTextView->SetDocPosL( editPos, EFalse ); + + TViewYPosQualifier yPosQualifier; + yPosQualifier.SetFillScreen(); + yPosQualifier.SetMakeLineFullyVisible(); + iTextView->HandleGlobalChangeL( yPosQualifier ); + + iHighLightedFieldPos = KMsgHighlightedFieldPosNone; + } + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::PostInsertEditorFormattingL +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::PostInsertEditorFormattingL( TBool aDuplicateEvent ) + { + if ( IsFocused() && !IsReadOnly() ) + { + TInt editPos = CursorPos(); + if ( editPos > TextLength() ) + { + return; + } + TChar prevChar = CharAtPos( editPos - 1 ); + + if ( !aDuplicateEvent && + ( prevChar == KSemicolon || prevChar == KArabicSemicolon ) ) + { + // user is at the end of a line, after a semicolon: + // paragraph delimiter after semicolon. + iTextView->SetDocPosL( editPos, EFalse ); + iRichText->CancelInsertCharFormat(); + InsertParagraphL( editPos++ ); + + TInt len = TextLength(); + if ( editPos > len ) + { + editPos = len; + } + + iTextView->SetDocPosL( editPos, EFalse ); + + TViewYPosQualifier yPosQualifier; + yPosQualifier.SetFillScreen(); + yPosQualifier.SetMakeLineFullyVisible(); + iTextView->HandleGlobalChangeL( yPosQualifier ); + + iHighLightedFieldPos = KMsgHighlightedFieldPosNone; + } + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CheckEditPermission +// +// Does edit permission check and returns its result. +// --------------------------------------------------------- +// +TUint32 CMsgAddressControlEditor::CheckEditPermission() const + { + TUint32 editPermission = CMsgBaseControl::EMsgEditNone; + TInt textLength = TextLength(); + TCursorSelection selection = iTextView->Selection(); + + if ( selection.Length() ) + { + TInt selLowerPos = selection.LowerPos(); + // The higherPos is more close to the end of the text + TInt selHigherPos = selection.HigherPos(); + TFindFieldInfo fieldInfoLower; + TFindFieldInfo fieldInfoHigher; + TBool lowerPosOnField( + iRichText->FindFields( fieldInfoLower, selLowerPos, 0 ) ); + TBool higherPosOnField( + iRichText->FindFields( fieldInfoHigher, selHigherPos, 0 ) ); + + if ( lowerPosOnField && higherPosOnField ) + { + // Both the lowerPos and the higherPos are on a field + if ( IsPosAtEntryBeginning( selLowerPos ) && + IsPosAtEntryBeginning( selHigherPos ) ) + { + // Both positions are at the beginning of the fields + editPermission |= CMsgAddressControl::EMsgEditParagraphDelimiter + | CMsgAddressControl::EMsgEditBackspace + | CMsgAddressControl::EMsgEditDelete + | CMsgBaseControl::EMsgEditCut; + } + } + else if ( lowerPosOnField ) + { + if ( IsPosAtEntryBeginning( selLowerPos ) ) + { + editPermission |= CMsgAddressControl::EMsgEditParagraphDelimiter + | CMsgAddressControl::EMsgEditBackspace + | CMsgAddressControl::EMsgEditCharInsert + | CMsgAddressControl::EMsgEditDelete + | CMsgBaseControl::EMsgEditCut + | CMsgBaseControl::EMsgEditPaste; + } + } + else if ( higherPosOnField ) + { + if ( IsPosAtEntryBeginning( selHigherPos ) ) + { + editPermission |= CMsgAddressControl::EMsgEditParagraphDelimiter; + } + } + else if ( IsFieldOnLeft( selLowerPos ) ) + { + // Neither position are on a field but the lower pos is + // just next to a field on the right side + editPermission |= CMsgAddressControl::EMsgEditParagraphDelimiter; + } + else + { + // Neither position are on a field + editPermission |= CMsgAddressControl::EMsgEditCharInsert + | CMsgAddressControl::EMsgEditParagraphDelimiter + | CMsgAddressControl::EMsgEditBackspace + | CMsgAddressControl::EMsgEditDelete + | CMsgBaseControl::EMsgEditCut + | CMsgBaseControl::EMsgEditPaste; + } + + // Copy is always possible + editPermission |= CMsgBaseControl::EMsgEditCopy; + + // BackspaceMove and RemoveEntry are never possible when selection is on + editPermission &= ~CMsgAddressControl::EMsgEditBackspaceMove; + editPermission &= ~CMsgAddressControl::EMsgEditRemoveEntry; + + editPermission |= + (selHigherPos - selLowerPos) == textLength ? + CMsgBaseControl::EMsgEditUnSelectAll : 0; + } + else + { + // Selection is off + TInt cursorPos = CursorPos(); + TBool posAtEntryBeginning = IsPosAtEntryBeginning( cursorPos ); + TChar ch = CharAtPos( cursorPos - 1 ); + TChar nextCh = CharAtPos( cursorPos ); + + if ( ch == CEditableText::EParagraphDelimiter ) + { + ch = CharAtPos( cursorPos - 2 ); + } + + TInt entryAtPos = EntryNumber( cursorPos ); + + if ( IsFieldOnPos( cursorPos ) && !posAtEntryBeginning ) + { + // The cursor is in a field. No edit actions are allowed. + editPermission = (TUint32) CMsgBaseControl::EMsgEditNone; + } + else if ( IsFieldOnLeft( cursorPos ) && !posAtEntryBeginning ) + { + // The cursor is not on a field but just next to it on the right + // side. Only the paragraph delimiter can be inserted or the field + // can be removed by the backspace. + editPermission |= CMsgAddressControl::EMsgEditParagraphDelimiter; + editPermission |= CMsgAddressControl::EMsgEditRemoveEntry; + editPermission |= CMsgAddressControl::EMsgEditCharInsert; + // This should check also a situation when next entry is empty + // and when delete is also possible. + } + else if ( ( ch == KSemicolon || ch == KArabicSemicolon + || IsPriorEntryVerified( entryAtPos ) ) && posAtEntryBeginning ) + { + // The cursor is not in a field but just below it at the + // beginning of the line. + if ( IsEntryEmpty( entryAtPos ) ) + { + // Current entry is empty. RemoveEntry and BackspaceMove + // the edit actions are not allowed. + editPermission = (TUint32) CMsgBaseControl::EMsgEditAll; + editPermission &= ~CMsgAddressControl::EMsgEditBackspaceMove; + editPermission &= ~CMsgAddressControl::EMsgEditRemoveEntry; + } + else + { + // Pressing the backspace button is allowed but no char + // removal i.e the BackspaceMove is allowed. + editPermission = (TUint32) CMsgBaseControl::EMsgEditAll; + editPermission &= ~CMsgAddressControl::EMsgEditBackspace; + editPermission &= ~CMsgAddressControl::EMsgEditRemoveEntry; + } + } + else + { + // The cursor is somewhere else but in a field or next to it on + // the right side. All but the RemoveEntry and BackspaceMove the + // edit actions are allowed. + editPermission = (TUint32) CMsgBaseControl::EMsgEditAll; + editPermission &= ~CMsgAddressControl::EMsgEditBackspaceMove; + editPermission &= ~CMsgAddressControl::EMsgEditRemoveEntry; + } + + if ( IsFieldOnRight( cursorPos ) ) + { + // Field immediatelly on right side of curser. Delete and RemoveEntry + // are allowed. + editPermission |= CMsgAddressControl::EMsgEditDelete; + editPermission |= CMsgAddressControl::EMsgEditRemoveEntry; + } + else if ( IsFieldOnRight( cursorPos + 1) && + nextCh == CEditableText::EParagraphDelimiter ) + { + // The cursor is not in a field but just above it at the + // end of the line. + if ( IsEntryEmpty( entryAtPos ) ) + { + // Current entry is empty. RemoveEntry are not allowed. + editPermission |= CMsgAddressControl::EMsgEditDelete; + editPermission &= ~CMsgAddressControl::EMsgEditRemoveEntry; + } + else + { + // Current entry is not empty. Delete is not allowed. + editPermission &= ~CMsgAddressControl::EMsgEditDelete; + } + } + + // Copy is never allowed when selection is off. + editPermission &= ~CMsgBaseControl::EMsgEditCopy; + // Cut is never allowed when selection is off. + editPermission &= ~CMsgBaseControl::EMsgEditCut; + // UnSelectAll is never allowed when selection is off. + editPermission &= ~CMsgBaseControl::EMsgEditUnSelectAll; + } + + editPermission &= ~CMsgBaseControl::EMsgEditUndo; // Undo is never allowed. + + if ( textLength ) + { + editPermission |= CMsgBaseControl::EMsgEditSelectAll; + } + else + { + editPermission &= ~CMsgBaseControl::EMsgEditSelectAll; + } + + if ( IsReadOnly() ) + { + editPermission &= ~CMsgBaseControl::EMsgEditPaste; + editPermission &= ~CMsgBaseControl::EMsgEditCut; + editPermission &= ~CMsgAddressControl::EMsgEditRemoveEntry; + editPermission &= ~CMsgAddressControl::EMsgEditParagraphDelimiter; + editPermission &= ~CMsgAddressControl::EMsgEditCharInsert; + } + + return editPermission; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::ConstructFromResourceL +// +// Creates this control from resource. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::ConstructFromResourceL( TResourceReader& aReader ) + { + // This used to be font id. Not used anymore. Have to read it, though. + /*TInt fontId =*/ aReader.ReadInt32(); + + ConstructL(); + + iAddressControlEditorFieldFactory = + new ( ELeave ) TMsgAddressControlEditorFieldFactory(); + + if ( !IsReadOnly() ) + { + ReadAknResourceL( aReader ); + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::SetAndGetSizeL +// +// Calculates and sets the size of the control and returns new size as +// reference aSize. Sets maximum height for editor to be maximum +// body height. If our height has already been set then uses the current +// one since edwin will decide internally what is the correct height and +// report this through HandleEdwinSizeEventL observer interface. +// After all this finally set the size to +// edwin. This might cause reformatting so after this we need to check +// whether we are band formatting and set the size to maximum body height +// if we are since edwin will not report height change through HandleEdwinSizeEventL +// when band formatting is enabled. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::SetAndGetSizeL( TSize& aSize ) + { + TSize controlSize( aSize ); + + if ( iSize.iHeight > 0 ) + { + controlSize.iHeight = iSize.iHeight; + } + + CMsgExpandableControlEditor::SetAndGetSizeL( aSize ); + + // Make sure that if we are band formatting editor size is set + // to maximum body height. Will not do any harm resetting this + // even if we already are at maximum body height. + if ( TextLayout() && TextLayout()->IsFormattingBand() ) + { + controlSize.iHeight = MaximumHeight(); + SetSizeWithoutNotification( controlSize ); + } + + aSize = iSize; + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::OfferKeyEventL +// +// Handles key event. +// --------------------------------------------------------- +// +TKeyResponse CMsgAddressControlEditor::OfferKeyEventL( + const TKeyEvent& aKeyEvent, TEventCode aType ) + { + if ( IsReadOnly() && + aKeyEvent.iCode == EKeyUpArrow && + iValidHighlightable ) + { + return EKeyWasConsumed; + } + return CMsgExpandableControlEditor::OfferKeyEventL( aKeyEvent, aType ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::HandlePointerEventL +// +// Handles pointer events. Sets handling pointer event flag on during +// edwin pointer event handling so that callbacks from edwin can +// use this information. +// In readonly mode: +// On button down event when automatic find is +// activated stores the previously focused field if there is one. +// On button up event checks whether same field is still focused. +// If user has tapped already focused field again selection key menu +// is launched by simulating key event into it. +// In non-readonly mode: +// If user taps in the middle of validated recipient field cursor +// is moved to the beginning of that field if it is not already located +// there. Also highlight is checked. +// --------------------------------------------------------- +// +#ifdef RD_SCALABLE_UI_V2 +void CMsgAddressControlEditor::HandlePointerEventL( const TPointerEvent& aPointerEvent ) + { + iHandlingPointerEvent = ETrue; + + TBool forwardRequest( ETrue ); + + if ( IsReadOnly() ) + { + if ( iValidHighlightable && + ( aPointerEvent.iType == TPointerEvent::EButton1Down || + aPointerEvent.iType == TPointerEvent::EButton1Up ) ) + { + TPoint tapPoint( aPointerEvent.iPosition ); + TInt docPos( TextView()->XyPosToDocPosL( tapPoint ) ); + + const CMsgAddressControlEditorField* currentField = FieldFromPos( docPos ); + + if ( aPointerEvent.iType == TPointerEvent::EButton1Down ) + { + iPreviousField = currentField; + + if ( iPreviousField && + !iPreviousField->IsVerified() ) + { + iPreviousField = NULL; + } + else + { + forwardRequest = EFalse; + } + } + else if ( aPointerEvent.iType == TPointerEvent::EButton1Up ) + { + if ( currentField && + iPreviousField == currentField ) + { + TKeyEvent event; + event.iCode = EKeyDevice3; + event.iScanCode = EStdKeyDevice3; + event.iModifiers = 0; + event.iRepeats = 0; + + iCoeEnv->WsSession().SimulateKeyEvent( event ); + + forwardRequest = EFalse; + } + } + } + } + else + { + if ( aPointerEvent.iType == TPointerEvent::EButton1Down || + aPointerEvent.iType == TPointerEvent::EButton1Up ) + { + TPoint tapPoint( aPointerEvent.iPosition ); + TInt docPos( TextView()->XyPosToDocPosL( tapPoint ) ); + + const CMsgAddressControlEditorField* currentField = FieldFromPos( docPos ); + + if ( currentField && + currentField->IsVerified() ) + { + // forwardRequest = EFalse; + TInt entryStartPos = EntryStartPos( EntryNumber( docPos ) ); + if ( entryStartPos != CursorPos() ) + { + SetCursorPosL( entryStartPos, EFalse ); + CheckHighlightingL(); + } + } + } + } + + if ( forwardRequest ) + { + CMsgExpandableControlEditor::HandlePointerEventL( aPointerEvent ); + } +#ifdef RD_TACTILE_FEEDBACK + else if(aPointerEvent.iType == TPointerEvent::EButton1Down) + { + MTouchFeedback* feedback = MTouchFeedback::Instance(); + if ( feedback ) + { + feedback->InstantFeedback( this, ETouchFeedbackBasic ); + } + } +#endif + + iHandlingPointerEvent = EFalse; + } +#else +void CMsgAddressControlEditor::HandlePointerEventL( const TPointerEvent& /*aPointerEvent*/ ) + { + } +#endif // RD_SCALABLE_UI_V2 + +// --------------------------------------------------------- +// CMsgAddressControlEditor::HandleResourceChange +// +// During layout switching if editor contains text highlight is +// turned off and entry string are rebuild as width of the editor +// might have changed. Also underlining is checked. +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::HandleResourceChange( TInt aType ) + { + if ( aType == KEikDynamicLayoutVariantSwitch ) + { + CMsgExpandableControlEditor::HandleResourceChange( aType ); + + //SKIM-7NKC4B + /*if ( TextLength() > 0 ) + { + TRAP_IGNORE( + { + TurnHighlightingOffL(); + ReBuildEntryStringsL(); + + CheckUnderliningL(); + + if ( IsFocused() ) + { + CheckHighlightingL( EFalse ); + } + } ); + } + */ + } + else + { + CMsgExpandableControlEditor::HandleResourceChange( aType ); + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::InsertParagraphL +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::InsertParagraphL( TInt aPos ) + { + iRichText->InsertL( aPos, CEditableText::EParagraphDelimiter ); + iTextView->HandleCharEditL( CTextLayout::EFParagraphDelimiter ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::InsertSemicolonL +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::InsertSemicolonL( TInt aPos ) + { + TInt langcode = CAknEnv::Static()->SettingCache().InputLanguage(); + TChar semicolon = ( langcode == ELangArabic ) + ? KArabicSemicolon + : KSemicolon; + + iRichText->InsertL( aPos, semicolon ); + iTextView->HandleCharEditL( CTextLayout::EFCharacterInsert ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CcpuPasteL +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::CcpuPasteL() + { + TInt cursorPos = CursorPos(); + TChar ch = CharAtPos ( cursorPos ); + + if ( ch == KSemicolon || ch == KArabicSemicolon ) + { + iRichText->CancelInsertCharFormat(); + iRichText->InsertL(cursorPos, CEditableText::EParagraphDelimiter); + iTextView->HandleCharEditL( CTextLayout::EFParagraphDelimiter ); + } + + CMsgExpandableControlEditor::CcpuPasteL(); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::SetAddressFieldAutoHighlight +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::SetAddressFieldAutoHighlight( TBool aValidHighlightable ) + { + if ( iValidHighlightable != aValidHighlightable && !aValidHighlightable ) + { + // Automatic find turned off -> cancel selection + TRAP_IGNORE( ClearSelectionL() ); + } + iValidHighlightable = aValidHighlightable; + + TRAP_IGNORE( CheckUnderliningL() ); + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::CheckUnderliningL +// +// +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::CheckUnderliningL() + { + TInt textLength = TextLength(); + TInt length = textLength; + TInt docPos = 0; + TFindFieldInfo fieldInfo; + + while ( length > 0 && + docPos < textLength && + iRichText->FindFields( fieldInfo, docPos, length ) ) + { + TCharFormat currentCharFormat; + TCharFormatMask currentCharMask; + + TCharFormat newCharFormat; + TCharFormatMask newCharMask; + + iRichText->GetCharFormat( currentCharFormat, + currentCharMask, + fieldInfo.iFirstFieldPos, + fieldInfo.iFirstFieldLen ); + + if ( iValidHighlightable ) + { + if ( currentCharFormat.iFontPresentation.iUnderline != EUnderlineOn ) + { + newCharFormat.iFontPresentation.iUnderline = EUnderlineOn; + newCharMask.SetAttrib( EAttFontUnderline ); + } + } + else + { + if ( currentCharFormat.iFontPresentation.iUnderline != EUnderlineOff ) + { + newCharFormat.iFontPresentation.iUnderline = EUnderlineOff; + newCharMask.SetAttrib( EAttFontUnderline ); + } + } + + if ( newCharMask.AttribIsSet( EAttFontUnderline ) ) + { + iRichText->ApplyCharFormatL( newCharFormat, + newCharMask, + fieldInfo.iFirstFieldPos, + fieldInfo.iFirstFieldLen ); + } + + if ( fieldInfo.iFieldCountInRange == 0 ) // no more fields left within a range + { + break; + } + + docPos = fieldInfo.iFirstFieldPos + fieldInfo.iFirstFieldLen; + length = textLength - docPos; + } + } + +// --------------------------------------------------------- +// CMsgAddressControlEditor::PrepareForReadOnlyL +// --------------------------------------------------------- +// +void CMsgAddressControlEditor::PrepareForReadOnlyL( TBool aReadOnly ) + { + if ( IsReadOnly() == aReadOnly ) + { + return; + } + + if ( aReadOnly ) + { +#ifdef RD_SCALABLE_UI_V2 + SetHighlightStyleL( EEikEdwinHighlightLink ); +#endif + + SetWordWrapL( EFalse ); + if ( AddressFieldAutoHighlight() ) + { + SelectAllL(); // for automatic highlight + } + } + + CMsgExpandableControlEditor::PrepareForReadOnlyL( aReadOnly ); + } + +// End of File