diff -r 000000000000 -r 72b543305e3a messagingappbase/msgeditor/viewsrc/MsgBodyControlEditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingappbase/msgeditor/viewsrc/MsgBodyControlEditor.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,710 @@ +/* +* 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: MsgBodyControlEditor implementation +* +*/ + + + +// ========== INCLUDE FILES ================================ + +#include // for CEikonEnv +#include // for CRichText +#include // for CClipboard +#include // for AknUtils +#include // for automatic highlight + +#include // LAF +#include + +#ifdef RD_TACTILE_FEEDBACK +#include +#endif + +#include "MsgEditorCommon.h" // +#include "MsgBodyControlEditor.h" // for CMsgBodyControlEditor +#include "MsgBaseControl.h" // for TMsgEditPermissionFlags +#include "MsgBaseControlObserver.h" // for MMsgBaseControlObserver +#include "MsgEditorCustomDraw.h" // for CMsgEditorCustomDraw + +// ========== EXTERNAL DATA STRUCTURES ===================== + +// ========== EXTERNAL FUNCTION PROTOTYPES ================= + +// ========== CONSTANTS ==================================== + +// ========== MACROS ======================================= + +// ========== LOCAL CONSTANTS AND MACROS =================== + +const TInt KBodyFullFormattingLength = 500; + +// ========== MODULE DATA STRUCTURES ======================= + +// ========== LOCAL FUNCTION PROTOTYPES ==================== + +// ========== LOCAL FUNCTIONS ============================== + +// ========== MEMBER FUNCTIONS ============================= + +// --------------------------------------------------------- +// CMsgBodyControlEditor::CMsgBodyControlEditor +// +// Constructor. +// --------------------------------------------------------- +// +CMsgBodyControlEditor::CMsgBodyControlEditor( const CCoeControl* aParent, + TUint32& aFlags, + MMsgBaseControlObserver* aBaseControlObserver ) : + CMsgExpandableControlEditor( aParent, aFlags, aBaseControlObserver ), + iPreviousItemStart( -1 ), + iPreviousItemLength( -1 ) + { + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::~CMsgBodyControlEditor +// +// Destructor. +// --------------------------------------------------------- +// +CMsgBodyControlEditor::~CMsgBodyControlEditor() + { + + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::ConstructL +// +// +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::ConstructL() + { + TInt edwinFlags = ENoHorizScrolling | + ENoAutoSelection | + EWidthInPixels | + EAllowUndo | + EResizable | + EPasteAsPlainText; + + iFlags |= EMsgControlModeDoNotDrawFrameBgContext; + ResolveLayouts(); + + /*if ( iBaseControlObserver && + !iBaseControlObserver->ViewInitialized() ) + { + SetSuppressFormatting( ETrue ); + }*/ + + iItemFinder = CItemFinder::NewL( CItemFinder::EUrlAddress | + CItemFinder::EEmailAddress | + CItemFinder::EPhoneNumber | + CItemFinder::EUriScheme ); + + iItemFinder->AddObserver( *this ); + iFocusChangedBeforeParseFinish = EFalse; + + CEikRichTextEditor::ConstructL( iParentControl, 0, iMaxNumberOfChars, edwinFlags ); + // 107-24185 : Emoticon support for SMS and MMS + AddFlagToUserFlags(EAvkonEnableSmileySupport); + + // Both global text editor and edwin have it's own formatting layers. + // CEikRichTextEditor::ConstructL call will set global formatting layers + // into use for text. This is not correct as edwin formatting layers + // will hold correct formatting values. Below calls will set edwin + // formatting layers into use and prevent client based formatting from + // being based on incorrect formatting layer. + SetParaFormatLayer( iEikonEnv->SystemParaFormatLayerL()->CloneL() ); + SetCharFormatLayer( iEikonEnv->SystemCharFormatLayerL()->CloneL() ); + + SetUpperFullFormattingLength( KBodyFullFormattingLength ); + + SetMaximumHeight( MsgEditorCommons::MaxBodyHeight() ); + + // Set the wrap width to be that of the current body text pane. + // This will make the formatting of text safer if it is performed + // before the editor has ever had SetSize() called. WrapWidth is + // usually set in HandleSizeChanged() + if ( iLayout ) + { + iLayout->SetWrapWidth( iTextLayout.TextRect().Width() ); + } + + TRect dataPane = MsgEditorCommons::MsgDataPane(); + + TMargins8 edwinMargins; + edwinMargins.iTop = 0; + edwinMargins.iBottom = 0; + edwinMargins.iLeft = (TInt8)( iTextLayout.TextRect().iTl.iX - dataPane.iTl.iX ); + edwinMargins.iRight = (TInt8)( dataPane.iBr.iX - iTextLayout.TextRect().iBr.iX ); + SetBorderViewMargins( edwinMargins ); + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::SetAndGetSizeL +// +// Calculates and sets the size of the control and returns new size as +// reference aSize. +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::SetAndGetSizeL( TSize& aSize ) + { + // This may change the heigh if the content has changed + SetSize( aSize ); + aSize = iSize; + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::InsertCharacterL +// +// Inserts a character to the editor. +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::InsertCharacterL( const TChar& aCharacter ) + { + + // 107-24185 : Emoticon support for SMS and MMS + + TBuf<1> text; + text.Append( aCharacter ); + InsertTextL( text ); + + //UpdateScrollBarsL(); + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::InsertTextL +// +// Inserts text to the editor. +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::InsertTextL( const TDesC& aText ) + { + if ( iTextView ) + { + TCursorSelection selection = iTextView->Selection(); + const TInt selLength = selection.Length(); + const TInt lower = selection.LowerPos(); + + if ( selLength ) + { + // 107-24185 : Emoticon support for SMS and MMS, symbian call + InsertDeleteCharsL( lower, aText, selection ); + } + else + { + InsertDeleteCharsL( selection.iCursorPos, aText, selection ); + } + + SetCursorPosL( lower + aText.Length(), EFalse ); + + NotifyNewFormatL(); + } + else + { + iText->InsertL( 0, aText ); + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::ActivateL +// +// This function is needed for overriding +// CMsgExpandableControlEditor::ActivateL(). +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::ActivateL() + { + if ( IsActivated() ) + { + return; + } + + CMsgExpandableControlEditor::ActivateL(); + + } + + +// --------------------------------------------------------- +// CMsgBodyControlEditor::OfferKeyEventL +// +// +// --------------------------------------------------------- +// +TKeyResponse CMsgBodyControlEditor::OfferKeyEventL( + const TKeyEvent& aKeyEvent, TEventCode aType ) + { + if ( IsReadOnly() ) + { + switch ( aKeyEvent.iCode ) + { + case EKeyUpArrow: + case EKeyDownArrow: + break; + + default: + return CEikRichTextEditor::OfferKeyEventL( aKeyEvent, aType ); + } + + SetHighlightL( CursorPos(), + ( aKeyEvent.iCode == EKeyUpArrow ) + ? EMsgFocusUp + : EMsgFocusDown ); + return EKeyWasConsumed; + } + else + { + return CEikRichTextEditor::OfferKeyEventL( aKeyEvent, aType ); + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::HandlePointerEventL +// +// Handles pointer events. +// --------------------------------------------------------- +// +#ifdef RD_SCALABLE_UI_V2 +void CMsgBodyControlEditor::HandlePointerEventL( const TPointerEvent& aPointerEvent ) + { + TBool forwardRequest( ETrue ); + + if ( IsActivated() && + IsReadOnly() && + ( aPointerEvent.iType == TPointerEvent::EButton1Down || + aPointerEvent.iType == TPointerEvent::EButton1Up ) ) + { + TPoint tapPoint( aPointerEvent.iPosition ); + TInt docPos( TextView()->XyPosToDocPosL( tapPoint ) ); + + TInt start( 0 ); + TInt length( 0 ); + MParser* parser = iItemFinder; + + TBool tappedOverTag( RichText()->CursorOverTag( docPos, parser, start, length ) ); + + if ( aPointerEvent.iType == TPointerEvent::EButton1Down ) + { + iPreviousItemStart = -1; + iPreviousItemLength = -1; + + if ( tappedOverTag ) + { + TPoint relativeTapPoint( aPointerEvent.iPosition - iPosition ); + if ( iItemFinder->ItemWasTappedL( relativeTapPoint ) ) + { + iPreviousItemStart = start; + iPreviousItemLength = length; + + forwardRequest = EFalse; + } + } + else + { + iItemFinder->ResetCurrentItem(); + } + } + else if ( aPointerEvent.iType == TPointerEvent::EButton1Up ) + { + if ( tappedOverTag && + start >= iPreviousItemStart && + length <= iPreviousItemLength ) + { + TKeyEvent event; + event.iCode = EKeyDevice3; + event.iScanCode = EStdKeyDevice3; + event.iModifiers = 0; + event.iRepeats = 0; + + iCoeEnv->WsSession().SimulateKeyEvent( event ); + + forwardRequest = EFalse; + } + } + } + + 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 // RD_TACTILE_FEEDBACK + } +#else +void CMsgBodyControlEditor::HandlePointerEventL( const TPointerEvent& /*aPointerEvent*/ ) + { + } +#endif // RD_SCALABLE_UI_V2 + + +// --------------------------------------------------------- +// CMsgBodyControlEditor::IsFocusChangePossibleL +// +// +// --------------------------------------------------------- +// +TBool CMsgBodyControlEditor::IsFocusChangePossibleL( + TMsgFocusDirection aDirection ) + { + CItemFinder::TFindDirection direction = aDirection == EMsgFocusUp + ? CItemFinder::ENextUp + : CItemFinder::ENextDown; + + TInt cursorPos = CursorPos(); + iItemFinder->ResolveAndSetItemTypeL( cursorPos ); + TInt nextItem = iItemFinder->PositionOfNextItem( direction ); + TPoint dummy; + + if ( nextItem < KErrNone ) + { + // Next item not found. + if ( aDirection == EMsgFocusUp ) + { + return TextLayout()->FirstDocPosFullyInBand() == 0; + } + else + { + return TextLayout()->PosInBand( TextLength(),dummy ); + } + } + else + { + // Next item found. -> Focus change not allowed. + return 0; + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::SetupAutomaticFindAfterFocusChangeL +// +// +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::SetupAutomaticFindAfterFocusChangeL( TBool aBeginning ) + { + iInitTop = aBeginning; + if ( !iTextParsed ) + { + iFocusChangedBeforeParseFinish = ETrue; + } + else + { + if ( aBeginning ) + { + SetHighlightL( 0, EMsgFocusDown, ETrue ); + } + else + { + SetHighlightL( TextLength(), EMsgFocusUp, ETrue ); + } + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::SetHighlightL +// +// +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::SetHighlightL( + TInt aCursorPos, TMsgFocusDirection aDirection, + TBool aInit /*= EFalse*/ ) + { + // Use "link highlight" + SetHighlightStyleL( EEikEdwinHighlightLink ); + TInt textLength = TextLength(); + + TCursorPosition::TMovementType movetype = aDirection == EMsgFocusUp + ? TCursorPosition::EFPageUp + : TCursorPosition::EFPageDown; + + CItemFinder::TFindDirection direction = aDirection == EMsgFocusUp + ? CItemFinder::ENextUp + : CItemFinder::ENextDown; + + TInt nextItem = KErrNotFound; + TPoint dummy; + if ( iTextParsed ) + { + if ( aInit ) + { + iItemFinder->NextItemOrScrollL( aCursorPos == 0 + ? CItemFinder::EInitDown + : CItemFinder::EInitUp ); + } + else + { + nextItem = iItemFinder->PositionOfNextItem( direction ); + } + } + + if ( nextItem > KErrNotFound ) + { + if ( !aInit && + !TextLayout()->PosInBand( nextItem ,dummy ) ) + { + // first move one page and then set selection + SetCursorPosL( CursorPos(), EFalse ); + iTextView->MoveCursorL( movetype, EFalse ); + } + + if ( TextLayout()->PosInBand( nextItem ,dummy ) ) + { + // next item is visible, just change selection + iItemFinder->ResolveAndSetItemTypeL( nextItem ); + + TInt selectionLength = iItemFinder->CurrentItemExt().iEnd - iItemFinder->CurrentItemExt().iStart + 1; + TCursorSelection cursorSelection; + cursorSelection.iCursorPos = nextItem; + cursorSelection.iAnchorPos = nextItem + selectionLength; + if ( !TextLayout()->PosInBand( nextItem + selectionLength, dummy ) ) + { + // Next item does not fit fully on the screen. + // Have to move page first. + SetCursorPosL( CursorPos(), EFalse ); + iTextView->MoveCursorL( movetype, EFalse ); + } + + iTextView->SetSelectionL( cursorSelection ); + } + + } + else + { + if ( !aInit ) + { + switch ( aDirection ) + { + case EMsgFocusDown: + if ( !TextLayout()->PosInBand( textLength, dummy) ) + { + iTextView->MoveCursorL( movetype, EFalse ); + + // Following is a bit tricky since MoveCursor looses + // selection so we have to set it again if previous + // selection still on the screen. + nextItem = iItemFinder->PositionOfNextItem( + CItemFinder::ENextUp ); + if ( nextItem > KErrNotFound ) + { + if ( TextLayout()->PosInBand( nextItem ,dummy ) ) + { + // next item is visible, change selection + iItemFinder->ResolveAndSetItemTypeL( nextItem ); + + TInt selectionLength = iItemFinder->CurrentItemExt().iEnd - iItemFinder->CurrentItemExt().iStart + 1; + TCursorSelection cursorSelection; + cursorSelection.iCursorPos = nextItem; + cursorSelection.iAnchorPos = nextItem + selectionLength; + iTextView->SetSelectionL( cursorSelection ); + } + else + { + nextItem = iItemFinder->PositionOfNextItem( + CItemFinder::ENextDown ); + } + } + } + break; + default: + case EMsgFocusUp: + if ( !TextLayout()->PosInBand( 0,dummy ) ) + { + iTextView->MoveCursorL( movetype, EFalse ); + } + break; + }; + + } + } + + } + + +// --------------------------------------------------------- +// CMsgBodyControlEditor::CursorInLastLine +// +// +// --------------------------------------------------------- +// +TBool CMsgBodyControlEditor::CursorInLastLine() + { + if (IsReadOnly()) + { + TPoint dummy; + return ( iItemFinder->PositionOfNextItem( CItemFinder::ENextDown ) == KErrNotFound + && TextLayout()->PosInBand( TextLength(), dummy ) ); + + } + else + { + return CMsgExpandableControlEditor::CursorInLastLine(); + } + + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::HandleParsingComplete +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::HandleParsingComplete() + { + TRAP_IGNORE( DoHandleParsingCompleteL() ); + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::DoHandleParsingCompleteL +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::DoHandleParsingCompleteL() + { + iTextParsed = ETrue; + // this needs to be changed so that bottom init can also be set + if ( iFocusChangedBeforeParseFinish ) + { + iFocusChangedBeforeParseFinish = EFalse; // just to make sure this is done only once + if ( iInitTop ) + { + SetHighlightL( 0, EMsgFocusDown, ETrue ); + } + else + { + SetHighlightL( TextLength(), EMsgFocusUp, ETrue ); + } + + if ( iBaseControlObserver ) + { + iBaseControlObserver->HandleBaseControlEventRequestL( NULL, + EMsgUpdateScrollbar ); + } + } + else + { + // In some cases the text view is left to some + // strange state. Make sure the view is in sync + // with cursor position. + SetCursorPosL( CursorPos(), EFalse ); + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::ResolveLayouts +// +// +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::ResolveLayouts() + { + TRect dataPane = MsgEditorCommons::MsgDataPane(); + + TAknLayoutRect msgTextPane; + msgTextPane.LayoutRect( + dataPane, + AknLayoutScalable_Apps::msg_text_pane( 0 ).LayoutLine() ); + TAknLayoutRect msgBodyPane; + msgBodyPane.LayoutRect( + msgTextPane.Rect(), + AknLayoutScalable_Apps::msg_body_pane().LayoutLine() ); + + if ( IsReadOnly() ) + { + iTextLayout.LayoutText( + msgBodyPane.Rect(), + AknLayoutScalable_Apps::msg_body_pane_t1( 0 ).LayoutLine() ); + } + else + { + iTextLayout.LayoutText( + msgBodyPane.Rect(), + AknLayoutScalable_Apps::msg_body_pane_t1( 1 ).LayoutLine() ); + } + + + if ( MsgEditorCustomDraw() ) + { + MsgEditorCustomDraw()->ResolveLayouts(); + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::LayoutEdwin +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::LayoutEdwin() + { + TAknLayoutRect msgTextPane; + msgTextPane.LayoutRect( MsgEditorCommons::MsgDataPane(), + AknLayoutScalable_Apps::msg_text_pane( 0 ).LayoutLine() ); + + TAknLayoutRect msgBodyPane; + msgBodyPane.LayoutRect( msgTextPane.Rect(), + AknLayoutScalable_Apps::msg_body_pane().LayoutLine() ); + + TRect parentRect( msgBodyPane.Rect() ); + parentRect.Move( 0, iParentControl->Position().iY ); + + DoLayoutEdwin( parentRect, + AknLayoutScalable_Apps::msg_body_pane_t1( 0 ).LayoutLine() ); + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::PrepareFocusTransition +// +// Do not change focus when automatic find parsing has been finished +// if focus transition out of this control has been performed. +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::PrepareFocusTransition() + { + if ( IsFocused() ) + { + iFocusChangedBeforeParseFinish = EFalse; + } + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::SetTextSkinColorIdL +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::SetTextSkinColorIdL() + { + CEikEdwin::SetTextSkinColorIdL( EAknsCIQsnTextColorsCG6 ); + } + +// --------------------------------------------------------- +// CMsgBodyControlEditor::HandleResourceChange +// +// Sets correct highlight extension. +// --------------------------------------------------------- +// +void CMsgBodyControlEditor::HandleResourceChange( TInt aType ) + { + CMsgExpandableControlEditor::HandleResourceChange( aType ); + + if ( aType == KAknsMessageSkinChange && MsgEditorCustomDraw() ) + { + MsgEditorCustomDraw()->SkinChanged();; + } + } + +// End of File