diff -r 000000000000 -r 72b543305e3a messagingappbase/msgeditor/viewsrc/MsgEditorView.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingappbase/msgeditor/viewsrc/MsgEditorView.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,3581 @@ +/* +* 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: MsgEditorView implementation* +*/ + + + +// ========== INCLUDE FILES ================================ + +#include // for CEikonEnv +#include // for CEikAppUi +#include // for TResourceReader +#include // for CEikRichTextEditor +#include +#include // for AknUtils +#include // for automatic highlight +#include +#include // LAF +#include + +#include // NearestLanguageFile +#include + +#include +#include +#include + +#include // for skins support +#include + +#include +#include + +#include "MsgEditorCommon.h" // +#include "MsgEditorView.h" // for CMsgEditorView +#include "MsgEditorObserver.h" // for MMsgEditorObserver +#include "MsgHeader.h" // for CMsgHeader +#include "MsgBody.h" // for CMsgBody +#include "MsgExpandableControl.h" // for MsgExpandableControl +#include "MsgBodyControl.h" // for MsgBodyControl +#include "MsgAddressControl.h" // for MsgAddressControl +#include "MsgAttachmentControl.h" // for MsgAttachmentControl +#include "MsgAddressControlEditor.h" // for MsgAddressControlEditor +#include "MsgEditorPanic.h" // for MsgEditor panics +#include "MsgEditorLogging.h" + +// ========== EXTERNAL DATA STRUCTURES ===================== + +// ========== EXTERNAL FUNCTION PROTOTYPES ================= + +// ========== CONSTANTS ==================================== + +// ========== MACROS ======================================= + +// ========== LOCAL CONSTANTS AND MACROS =================== + +const TInt KMsgControlIndexHeader = 0; +const TInt KMsgControlIndexBody = 1; +const TInt KMsgNumberOfControls = 2; + +const TInt KMsgMaximumScrollPartLength = 64; +_LIT( KMsgEditorAppUiResourceFileName, "msgeditorappui.rsc" ); + +// ========== MODULE DATA STRUCTURES ======================= + +// ========== LOCAL FUNCTION PROTOTYPES ==================== + +// ========== LOCAL FUNCTIONS ============================== + +// ========== MEMBER FUNCTIONS ============================= + +// --------------------------------------------------------- +// CMsgEditorView::CMsgEditorView +// +// Constructor. +// --------------------------------------------------------- +// +CMsgEditorView::CMsgEditorView( MMsgEditorObserver& aObserver, + TUint32 aEditorModeFlags ): + iEditorObserver( aObserver ), + iCurrentFocus( EMsgNoneFocused ), + iEditorModeFlags( aEditorModeFlags ), + iLineHeight( MsgEditorCommons::MsgBaseLineDelta() ), + iBaseLineOffset( MsgEditorCommons::MsgBaseLineOffset() ), + iResourceLoader( *iCoeEnv ) + { + } + +// --------------------------------------------------------- +// CMsgEditorView::ConstructL +// +// Constructs the editor view by creating the window, the header and the body. +// --------------------------------------------------------- +// +void CMsgEditorView::ConstructL() + { + CreateWindowL(); + + if ( AknStatuspaneUtils::FlatLayoutActive() ) + { + CEikStatusPane* statusPane = iEikonEnv->AppUiFactory()->StatusPane(); + if ( statusPane ) + { + CAknNavigationControlContainer* naviPane = + static_cast( statusPane->ControlL( + TUid::Uid( EEikStatusPaneUidNavi ) ) ); + + if ( naviPane ) + { + naviPane->SetPreferredNaviDecoratorLayoutStyle( + CAknNavigationDecorator::ENaviControlLayoutNormal ); + } + } + } + + iBgContext = CAknsBasicBackgroundControlContext::NewL( KAknsIIDQsnBgAreaMainMessage, + Rect(), + EFalse ); + + // T9: Add control to stack to make FEP work. View does not accept key + // events from CT9FepControl::HandleChangeInFocus(). Key events are + // handled first by editor app ui which routes them to view. + iEikonEnv->EikAppUi()->AddToStackL( this, + ECoeStackPriorityDefault, + ECoeStackFlagRefusesAllKeys ); + + Window().SetShadowDisabled( ETrue ); + + // Disables automatic background color drawing + // on window server. + Window().SetBackgroundColor(); + + CreateScrollBarL(); + CreateHeaderL(); + CreateBodyL(); + } + +// --------------------------------------------------------- +// CMsgEditorView::~CMsgEditorView +// +// Destructor. +// --------------------------------------------------------- +// +EXPORT_C CMsgEditorView::~CMsgEditorView() + { + iEikonEnv->EikAppUi()->RemoveFromStack( this ); + + delete iHeader; + delete iBody; + delete iScrollBar; + delete iBgContext; + + iResourceLoader.Close(); + + delete iScrollPopText; + } + +// --------------------------------------------------------- +// CMsgEditorView::NewL +// +// Factory method. +// --------------------------------------------------------- +// +EXPORT_C CMsgEditorView* CMsgEditorView::NewL( MMsgEditorObserver& aObserver, + TUint32 aEditorModeFlags ) + { + CMsgEditorView* self = new( ELeave ) CMsgEditorView( aObserver, aEditorModeFlags ); + + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + return self; + } + +// --------------------------------------------------------- +// CMsgEditorView::MopNext +// +// From MObjectProvider. +// --------------------------------------------------------- +// +EXPORT_C MObjectProvider* CMsgEditorView::MopNext() + { + return iEikonEnv->EikAppUi(); + } + +// --------------------------------------------------------- +// CMsgEditorView::ExecuteL +// +// Prepares the editor view for showing it on the screen. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::ExecuteL( const TRect& aRect, + TInt aControlIdForFocus ) + { + TRect rect( aRect ); + rect.iBr.iY = rect.iTl.iY + MsgEditorCommons::EditorViewHeigth(); + + AdjustComponentDistances(); + + SetViewRect( rect ); + iSize = rect.Size(); + + // Set initial size for controls. + TSize editorSize( rect.Size() ); + SetAndGetSizeL( editorSize, ETrue ); + + // Enable here so that size affecting events are processed + // from header & body controls after this point. + iStateFlags |= EMsgStateInitializing; + + SizeChanged(); + ActivateL(); + + iScrollParts = 1; + + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgScrollParts, + &iScrollParts, + NULL, + NULL ); + + if ( aControlIdForFocus != EMsgComponentIdNull ) + { + SetFocusByControlIdL( aControlIdForFocus ); + } + + if ( aControlIdForFocus == EMsgComponentIdNull || + iCurrentFocus == EMsgNoneFocused ) + { + iCurrentFocus = EMsgHeaderFocused; + + TInt newFocus = iHeader->FirstFocusableControl( 0, EMsgFocusDown ); + if ( newFocus == KErrNotFound ) + { + iCurrentFocus = EMsgBodyFocused; + newFocus = iBody->FirstFocusableControl( 0, EMsgFocusDown ); + + if ( newFocus == KErrNotFound ) + { + iCurrentFocus = EMsgNoneFocused; + } + else + { + iBody->ChangeFocusTo( newFocus, ENoDrawNow ); + } + } + else + { + iHeader->ChangeFocusTo( newFocus, ENoDrawNow ); + } + } + + UpdateScrollBarL(); + + SetComponentsInitialized(); + + iStateFlags &= ~EMsgStateInitializing; + iStateFlags |= EMsgStateInitialized; + + // Has to be called after EMsgStateInitialized flag is set on since + // otherwise control that have suppress text formatting are not + // resized correctly. + NotifyControlsForEvent( EMsgViewEventPrepareForViewing, 0 ); + + DrawNow(); + iCoeEnv->WsSession().Flush(); + } + +// --------------------------------------------------------- +// CMsgEditorView::ControlById +// +// Finds a control from the header and the body by id and returns a pointer to it. +// --------------------------------------------------------- +// +EXPORT_C CMsgBaseControl* CMsgEditorView::ControlById( TInt aControlId ) const + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + CMsgBaseControl* component = iHeader->Component( aControlId ); + + if ( !component ) + { + component = iBody->Component( aControlId ); + } + + return component; + } + +// --------------------------------------------------------- +// CMsgEditorView::FocusedControl +// +// Returns a pointer to a focused control. If no control is focused, returns NULL. +// --------------------------------------------------------- +// +EXPORT_C CMsgBaseControl* CMsgEditorView::FocusedControl() const + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + if ( iCurrentFocus == EMsgHeaderFocused ) + { + return iHeader->FocusedControl(); + } + else if ( iCurrentFocus == EMsgBodyFocused ) + { + return iBody->FocusedControl(); + } + else + { + return NULL; + } + } + +// --------------------------------------------------------- +// CMsgEditorView::SetFocus +// +// Sets focus to a control aControlId. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::SetFocus( TInt aControlId ) + { + __ASSERT_DEBUG( iStateFlags & EMsgStateInitialized, + Panic( EMsgFunctionCalledBeforeInitialization ) ); + + TRAP_IGNORE( + { + SetFocusByControlIdL( aControlId ); + UpdateScrollBarL(); + } ); + } + +// --------------------------------------------------------- +// CMsgEditorView::AddControlFromResourceL +// +// Constructs a control of type aControlType from resource and adds it to +// a form component aFormComponent. The control is added to a position aIndex. +// +// Leaves with KErrNotFound if aControlType is incorrect. +// --------------------------------------------------------- +// +EXPORT_C TInt CMsgEditorView::AddControlFromResourceL( TInt aResourceId, + TInt aControlType, + TInt aIndex, + TMsgFormComponent aFormComponent ) + { + CMsgBaseControl* control = NULL; + + switch (aControlType) + { + case EMsgExpandableControl: + { + control = new ( ELeave ) CMsgExpandableControl( *this ); + break; + } + case EMsgAddressControl: + { + control = new ( ELeave ) CMsgAddressControl( *this ); + break; + } + case EMsgAttachmentControl: + { + control = new ( ELeave ) CMsgAttachmentControl( *this, *this ); + break; + } + default: + { + control = iEditorObserver.CreateCustomControlL( aControlType ); + if ( control == NULL ) + { + User::Leave( KErrNotSupported ); + } + break; + } + } + + CleanupStack::PushL( control ); + control->SetControlType( aControlType ); + control->SetMopParent( this ); + + control->ConstructFromResourceL( aResourceId ); + + TInt controlId = control->ControlId(); + DoAddControlL( control, controlId, aIndex, aFormComponent ); + CleanupStack::Pop(); //control + + return controlId; + } + +// --------------------------------------------------------- +// CMsgEditorView::AddControlL +// +// Adds a control given by aControl to a form component aFormComponent. +// The control is added to a position aIndex with control id aControlId. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::AddControlL( CMsgBaseControl* aControl, + TInt aControlId, + TInt aIndex, + TMsgFormComponent aFormComponent ) + { + DoAddControlL( aControl, aControlId, aIndex, aFormComponent ); + } + +// --------------------------------------------------------- +// CMsgEditorView::RemoveControlL +// +// Removes the control aControlId from the header or the body and returns pointer +// to it. If a control cannot be found, returns NULL. +// --------------------------------------------------------- +// +EXPORT_C CMsgBaseControl* CMsgEditorView::RemoveControlL( TInt aControlId ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + if ( iStateFlags & EMsgStateInitialized ) // Control is removed on run-time. + { + if ( FocusedControl() == ControlById( aControlId ) && + iHeader->CountMsgControls() + iBody->CountMsgControls() > 1 ) + { + // Try to move focus to different control as focused control is removed and + // there is more than one control loaded. + MMsgEditorObserver::TMsgFocusEvent focusEvent = MMsgEditorObserver::EMsgFocusNone; + + if ( !RotateFocusL( EMsgFocusUp, focusEvent ) ) + { + if ( !RotateFocusL( EMsgFocusDown, focusEvent ) ) + { + //__ASSERT_DEBUG(EFalse, Panic(EMsgFocusLost)); + // There are no focusing component left. Remove the focus from current control. + FocusedControl()->SetFocus( EFalse ); + } + } + + iCoeEnv->SyncNotifyFocusObserversOfChangeInFocus(); + } + } + + // Remove a component from the header. + CMsgBaseControl* control = iHeader->RemoveControlL( aControlId ); + + if ( control == NULL ) + { + // If a component could not be found in the header, remove it from the body. + control = iBody->RemoveControlL( aControlId ); + } + + if ( iStateFlags & EMsgStateInitialized ) // Control is removed on run-time. + { + if ( control != NULL ) + { + CleanupStack::PushL( control ); + RefreshViewL(); + EnsureCorrectViewPosition(); + CleanupStack::Pop(); // control + } + } + else if ( iHeader->CountMsgControls() == 0 ) + { + iFormOffset = 0; + } + + if ( control ) + { + // This is needed atleast for japanese pictographs to not + // draw into current control area. + control->MakeVisible( EFalse ); + } + + return control; + } + +// --------------------------------------------------------- +// CMsgEditorView::DeleteControlL +// +// Removes the control aControlId from the header or the body and returns pointer +// to it. If a control cannot be found, returns NULL. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::DeleteControlL( TInt aControlId ) + { + CMsgBaseControl* baseControl = RemoveControlL( aControlId ); + delete baseControl; + } + +// --------------------------------------------------------- +// CMsgEditorView::FormComponent +// +// Returns a reference to a form component. +// --------------------------------------------------------- +// +EXPORT_C CCoeControl& CMsgEditorView::FormComponent( TMsgFormComponent aFormComponent ) const + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + switch ( aFormComponent ) + { + case EMsgHeader: + { + return *iHeader; + } + case EMsgBody: + { + return *iBody; + } + default: + { + __ASSERT_DEBUG( EFalse, Panic( EMsgIncorrectFormComponent ) ); + // Just to make compiler happy. + return *iBody; + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleScreenSizeChangeL +// +// Prepares the editor view for viewing it on the screen after screen size change. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::HandleScreenSizeChangeL( const TRect& aRect ) + { + __ASSERT_DEBUG( iStateFlags & EMsgStateInitialized, + Panic( EMsgFunctionCalledBeforeInitialization ) ); + + SetViewRect( aRect ); + RefreshViewL(); + } + + +// --------------------------------------------------------- +// CMsgEditorView::ResetControls +// +// Reset all controls. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::ResetControls() + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + iHeader->ResetControls(); + iBody->ResetControls(); + } + +// --------------------------------------------------------- +// CMsgEditorView::ResetControls +// +// Reset controls from header or body. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::ResetControls( TMsgFormComponent aFormComponent ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + switch ( aFormComponent ) + { + case EMsgHeader: + { + iHeader->ResetControls(); + break; + } + case EMsgBody: + { + iBody->ResetControls(); + break; + } + default: + { + __ASSERT_DEBUG( EFalse, Panic( EMsgIncorrectFormComponent ) ); + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::IsAnyControlModified +// +// Checks if any control owned by view is modified. +// --------------------------------------------------------- +// +EXPORT_C TBool CMsgEditorView::IsAnyControlModified() const + { + TInt i; + CMsgBaseControl* ctrl; + TInt countHeader = iHeader->CountMsgControls(); + TInt countBody = iBody->CountMsgControls(); + + for ( i = 0; i < countHeader; i++ ) + { + ctrl = iHeader->MsgControl( i ); + if ( ctrl && ctrl->IsModified() ) + { + return ETrue; + } + } + + for ( i = 0; i < countBody; i++ ) + { + ctrl = iBody->MsgControl( i ); + if ( ctrl && ctrl->IsModified() ) + { + return ETrue; + } + } + + return EFalse; + } + +// --------------------------------------------------------- +// CMsgEditorView::SetControlsModified +// +// Updates modified flag of controls owned by view. +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::SetControlsModified( TBool aFlag ) + { + TInt i; + CMsgBaseControl* ctrl; + TInt countHeader = iHeader->CountMsgControls(); + TInt countBody = iBody->CountMsgControls(); + + for ( i = 0; i < countHeader; i++ ) + { + ctrl = iHeader->MsgControl( i ); + if ( ctrl ) + { + ctrl->SetModified( aFlag ); + } + } + + for ( i = 0; i < countBody; i++ ) + { + ctrl = iBody->MsgControl( i ); + if ( ctrl ) + { + ctrl->SetModified( aFlag ); + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::OfferKeyEventL +// +// Handles key events. +// --------------------------------------------------------- +// +EXPORT_C TKeyResponse CMsgEditorView::OfferKeyEventL( const TKeyEvent& aKeyEvent, + TEventCode aType ) + { + CMsgBaseControl* ctrl = FocusedControl(); + + // Is this needed? + if ( ctrl && ctrl->ControlType() == EMsgAddressControl && aType == EEventKeyUp ) + { + return ctrl->OfferKeyEventL( aKeyEvent, aType ); + } + + if ( ( !( iStateFlags & EMsgStateInitialized ) ) || ( aType != EEventKey ) ) + { + return EKeyWasNotConsumed; + } + + TKeyResponse keyResp( EKeyWasNotConsumed ); + TBool focusRotated = EFalse; + TBool forceScrollUp = EFalse; + MMsgEditorObserver::TMsgFocusEvent focusEvent = MMsgEditorObserver::EMsgFocusNone; + + switch ( aKeyEvent.iCode ) + { + case EKeyUpArrow: + { + if ( ctrl ) + { + // No focus change allowed if selection is ongoing. + if ( !( aKeyEvent.iModifiers & EModifierShift ) && + ctrl->IsFocusChangePossible( EMsgFocusUp ) ) + { + if ( RotateFocusL( EMsgFocusUp, focusEvent ) ) + { + focusRotated = ETrue; + keyResp = EKeyWasConsumed; // focus changed. + } + else + { + // the cursor is in the topmost control. + TInt delta = -iFormOffset; + ScrollForm( delta, ETrue ); + + keyResp = EKeyWasNotConsumed; + } + } + } + else + { + // no focused control: set focus event if many parts. + if ( iScrollParts > 1 ) + { + focusEvent = MMsgEditorObserver::EMsgFocusAtTop; + } + } + break; + } + case EKeyDownArrow: + { + if ( ctrl ) + { + // No focus change allowed if selection is ongoing. + if ( !( aKeyEvent.iModifiers & EModifierShift ) && + ctrl->IsFocusChangePossible( EMsgFocusDown ) ) + { + if ( RotateFocusL( EMsgFocusDown, focusEvent ) ) + { + focusRotated = ETrue; + keyResp = EKeyWasConsumed; + } + else + { + keyResp = EKeyWasNotConsumed; + } + } + } + else + { + // no focused control: set focus event if many parts. + if ( iScrollParts > 1 ) + { + focusEvent = MMsgEditorObserver::EMsgFocusAtBottom; + } + } + break; + } + default: + { + break; + } + } + + if ( ctrl && keyResp == EKeyWasNotConsumed ) + { + keyResp = ctrl->OfferKeyEventL( aKeyEvent, aType ); + } + + if ( focusEvent != MMsgEditorObserver::EMsgFocusNone ) + { + MMsgEditorObserver::TMsgAfterFocusEventFunc after = + MMsgEditorObserver::EMsgAfterFocusNone; + +#ifdef RD_SCALABLE_UI_V2 + if ( focusEvent == MMsgEditorObserver::EMsgFocusAtTop ) + { + iVisiblePart--; + } + else if ( focusEvent == MMsgEditorObserver::EMsgFocusAtBottom ) + { + iVisiblePart++; + } + + iVisiblePart = Max( 0, Min( iScrollParts - 1, iVisiblePart ) ); +#endif + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgHandleFocusChange, + &focusEvent, + &after, + &iVisiblePart ); + + if ( after != MMsgEditorObserver::EMsgAfterFocusNone ) + { + forceScrollUp = SetAfterFocusL( after ); + } + } + + EnsureCorrectFormPosition( ( aKeyEvent.iCode == EKeyDownArrow ) && focusRotated, forceScrollUp ); + + UpdateScrollBarL(); + + return keyResp; + } + +// --------------------------------------------------------- +// CMsgEditorView::HandlePointerEventL +// +// Handles pointer event processing and propagation. +// --------------------------------------------------------- +// +#ifdef RD_SCALABLE_UI_V2 +void CMsgEditorView::HandlePointerEventL( const TPointerEvent& aPointerEvent ) + { + if ( AknLayoutUtils::PenEnabled() ) + { + TBool handled( EFalse ); + + if ( IsFocused() ) + { + TPointerEvent pointerEvent( aPointerEvent ); + + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgControlPointerEvent, + ControlFromPosition( aPointerEvent.iPosition, ETrue ), + &pointerEvent, + &handled ); + } + + if ( !handled ) + { + CCoeControl::HandlePointerEventL( aPointerEvent ); + } + } + } +#else +void CMsgEditorView::HandlePointerEventL( const TPointerEvent& /*aPointerEvent*/ ) + { + } +#endif // RD_SCALABLE_UI_V2 + + +// --------------------------------------------------------- +// CMsgEditorView::ViewInitialized +// +// Returns whether view is initialized. +// --------------------------------------------------------- +// +TBool CMsgEditorView::ViewInitialized() const + { + return iStateFlags & EMsgStateInitialized; + } + +// --------------------------------------------------------- +// CMsgEditorView::ViewRect +// +// Returns the current view rectangle. +// --------------------------------------------------------- +// +TRect CMsgEditorView::ViewRect() const + { + return iViewRect; + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleBaseControlEventRequestL +// +// Handles an event from a control. +// --------------------------------------------------------- +// +TBool CMsgEditorView::HandleBaseControlEventRequestL( CMsgBaseControl* aControl, + TMsgControlEventRequest aRequest, + TInt aDelta ) + { + if ( !( iStateFlags & EMsgStateInitialized ) ) + { + // Allow processing of possible height & position changing + // events when view is currently being initialized. + if ( !( iStateFlags & EMsgStateInitializing ) || + !( aRequest == EMsgEnsureCorrectFormPosition || + aRequest == EMsgHeightChanged ) ) + { + return ETrue; + } + } + + TBool done( EFalse ); + MMsgEditorObserver::TMsgFocusEvent focusEvent = MMsgEditorObserver::EMsgFocusNone; + + switch ( aRequest ) + { + case EMsgCheckIfFocusIsAboveThis: + { + TBool isAbove = EFalse; + TInt componentIndex = iHeader->ComponentIndexFromId( aControl->ControlId() ); + if ( componentIndex != KErrNotFound ) + { + // control found in header. + isAbove = iCurrentFocus == EMsgHeaderFocused + ? iHeader->CurrentFocus() < componentIndex + : EFalse; + } + else + { + // control found in body. + componentIndex = iBody->ComponentIndexFromId( aControl->ControlId() ); + isAbove = iCurrentFocus == EMsgBodyFocused + ? iBody->CurrentFocus() < componentIndex + : ETrue; + } + return isAbove; + } + + case EMsgEnsureCorrectFormPosition: + { + if ( iStateFlags & EMsgEnsureCorrectFormPositionRequestIssued ) + { + // prevents recursion. + return EFalse; + } + break; + } + case EMsgRotateFocusUp: + { + done = RotateFocusL( EMsgFocusUp, focusEvent ); + break; + } + case EMsgRotateFocusDown: + { + done = RotateFocusL( EMsgFocusDown, focusEvent ); + break; + } + case EMsgHeightChanged: + { + if ( iStateFlags & EMsgStateRefreshing ) + { + done = ETrue; + } + else if ( aControl && + aControl->ControlModeFlags() & EMsgControlModeBodyMaxHeight ) + { + // Special handling is needed when maximum height body control's height is changed. + // Currently this is happening when control is reseted. Special handling is + // needed as last line of body control's text should be shown on the + // last visible editor line if editor total height permits it. If editor total + // does not permit this then editor should show it's topmost line. + TInt bottomYPos( aControl->Position().iY + aControl->VirtualHeight() ); + TInt distanceFromBottom( Rect().iBr.iY - bottomYPos ); + + if ( distanceFromBottom > 0 ) + { + TInt delta = Abs( iFormOffset ) < distanceFromBottom ? -iFormOffset : + distanceFromBottom; + + if ( delta ) + { + ScrollForm( delta, ETrue ); + } + } + + if ( ViewInitialized() ) + { + UpdateScrollBarL(); + } + + done = ETrue; + } + else + { + done = HandleHeightChangedL( aControl, aDelta ); + + if ( ViewInitialized() ) + { + UpdateScrollBarL(); + } + } + return done; + } + case EMsgScrollForm: + { + done = ScrollForm( aDelta, ETrue ); + break; + } + case EMsgUpdateScrollbar: + { + UpdateScrollBarL(); + return ETrue; + } + default: + { + break; + } + } + + done = EnsureCorrectFormPosition( EFalse ); + + if ( iStateFlags & EMsgStateRefreshing || + iStateFlags & EMsgStateInitializing ) + { + return done; + } + + UpdateScrollBarL(); + + return done; + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleBaseControlEventRequestL +// +// Handles an event from a control. +// --------------------------------------------------------- +// +TBool CMsgEditorView::HandleBaseControlEventRequestL( CMsgBaseControl* aControl, + TMsgControlEventRequest aRequest ) + { + return HandleBaseControlEventRequestL( aControl, aRequest, 0 ); + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleEditObserverEventRequestL +// +// Handles an event from a control. +// --------------------------------------------------------- +// +TInt CMsgEditorView::HandleEditObserverEventRequestL( const CCoeControl* /*aControl*/, + TMsgControlEventRequest aRequest, + TAny* aArg1, + TAny* aArg2, + TAny* aArg3 ) + { + switch ( aRequest ) + { + case EMsgDenyCut: + { + CEikEdwin::TClipboardFunc cb = CEikEdwin::ECut; + TInt arg = 0; + + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgDenyClipboardOperation, + &cb, + &arg, + NULL ); + return arg; + } + case EMsgDenyCopy: + { + CEikEdwin::TClipboardFunc cb = CEikEdwin::ECopy; + TInt arg = 0; + + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgDenyClipboardOperation, + &cb, + &arg, + NULL ); + return arg; + } + case EMsgDenyPaste: + { + CEikEdwin::TClipboardFunc cb = CEikEdwin::EPaste; + TInt arg = 0; + + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgDenyClipboardOperation, + &cb, + &arg, + NULL ); + return arg; + } + case EMsgStateFlags: + { + return iStateFlags; + } + case EMsgGetNaviIndicators: + { + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgGetNaviIndicators, + aArg1, + aArg2, + aArg3 ); + return 0; + } + case EMsgButtonPressed: + { + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgButtonEvent, + aArg1, + aArg2, + aArg3 ); + return 0; + } + default: + { + break; + } + } + return 0; + } + +// --------------------------------------------------------- +// CMsgEditorView::CountComponentControls +// +// Returns a number of controls. +// --------------------------------------------------------- +// +TInt CMsgEditorView::CountComponentControls() const + { + TInt countScrollBarComponents( 0 ); + + if ( iScrollBar ) + { + countScrollBarComponents = iScrollBar->CountComponentControls(); + } + + return KMsgNumberOfControls + countScrollBarComponents; + } + +// --------------------------------------------------------- +// CMsgEditorView::ComponentControl +// +// Returns a control of index aIndex. +// --------------------------------------------------------- +// +CCoeControl* CMsgEditorView::ComponentControl( TInt aIndex ) const + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + TInt countScrollBarComponents( 0 ); + + if ( iScrollBar ) + { + countScrollBarComponents = iScrollBar->CountComponentControls(); + } + + switch ( aIndex ) + { + case KMsgControlIndexHeader: + { + return iHeader; + } + case KMsgControlIndexBody: + { + return iBody; + } + default: + { + if ( iScrollBar && + aIndex >= KMsgNumberOfControls && + aIndex < countScrollBarComponents + KMsgNumberOfControls ) + { + return iScrollBar->ComponentControl( aIndex - KMsgNumberOfControls ); + } + else + { + __ASSERT_DEBUG( EFalse, Panic( EMsgIncorrectComponentIndex ) ); + } + return NULL; + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::SizeChanged +// +// Sets new position for all the controls. +// --------------------------------------------------------- +// +void CMsgEditorView::SizeChanged() + { + MEBLOGGER_ENTERFN( "CMsgEditorView::SizeChanged" ); + + if ( iBgContext ) + { + iBgContext->SetRect( Rect() ); + iBgContext->SetParentPos( PositionRelativeToScreen() ); + } + + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + TPoint bodyPosition( MsgEditorCommons::MsgBodyPane().iTl.iX, 0 ); + TPoint headerPosition( MsgEditorCommons::MsgHeaderPane().iTl.iX, iFormOffset ); + + //MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::SizeChanged: header height %d "), iHeader->Size().iHeight); + //MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::SizeChanged: body height %d "), iBody->Size().iHeight); + + iHeader->SetExtent( headerPosition, iHeader->Size() ); + bodyPosition.iY = iHeader->Rect().iBr.iY /* + 1 */; + iBody->SetExtent( bodyPosition, iBody->Size() ); + + MEBLOGGER_LEAVEFN( "CMsgEditorView::SizeChanged" ); + } + +// --------------------------------------------------------- +// CMsgEditorView::FocusChanged +// +// Sets focus off or on from focused control. This is called +// when e.g. options menu is shown. Removed e.g. cursor from +// text fields. +// --------------------------------------------------------- +// +void CMsgEditorView::FocusChanged( TDrawNow /*aDrawNow*/ ) + { + if ( iHeader && iBody ) + { + CMsgBaseControl* ctrl = FocusedControl(); + + if ( ctrl ) + { + if ( !( ctrl->IsNonFocusing() ) ) + { + ctrl->SetFocus( IsFocused(), IsFocused() ? EDrawNow: ENoDrawNow ); + } + } + + if ( IsFocused() ) + { + NotifyControlsForEvent( EMsgViewEventViewFocusGain, 0 ); + } + else + { + NotifyControlsForEvent( EMsgViewEventViewFocusLost, 0 ); + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleResourceChange +// +// +// --------------------------------------------------------- +// +void CMsgEditorView::HandleResourceChange( TInt aType ) + { + // Updates child control layouts. Must be called before + // layout switch is handled here as AdjustComponentDistances + // expects controls to have correct height. Child control + // height may vary if layout is using different font. + CCoeControl::HandleResourceChange( aType ); + + if ( aType == KEikDynamicLayoutVariantSwitch ) + { + iScrollBar->SetScrollBarFrameObserver( this ); + + TInt invisibleLines( iFormOffset / iLineHeight ); + + iLineHeight = MsgEditorCommons::MsgBaseLineDelta(); + iBaseLineOffset = MsgEditorCommons::MsgBaseLineOffset(); + + // Correct the form offset to be multiple of current line height + iFormOffset = invisibleLines * iLineHeight; + + TRect mainPane = MsgEditorCommons::MsgMainPane(); + SetViewRect( mainPane ); + iSize = mainPane.Size(); + + AknLayoutUtils::LayoutVerticalScrollBar( + iScrollBar, + TRect( TPoint( 0, 0 ), mainPane.Size() ), + AknLayoutScalable_Apps::scroll_pane_cp017().LayoutLine() ); + + TRAP_IGNORE( RefreshViewL() ); + + EnsureCorrectViewPosition(); + } + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleControlEventL +// --------------------------------------------------------- +// +#ifdef RD_SCALABLE_UI_V2 +void CMsgEditorView::HandleControlEventL( CCoeControl* aControl, TCoeEvent aEventType ) + { + if ( aControl != iHeader && + aControl != iBody ) + { + switch( aEventType ) + { + case MCoeControlObserver::EEventPrepareFocusTransition: + { + // These things must be done before pointer event is handled. + CMsgBaseControl* baseControl = static_cast( aControl ); + + if ( baseControl && + ( baseControl->ControlType() == EMsgBodyControl || + baseControl->ControlType() == EMsgXhtmlBodyControl ) && + baseControl->ItemFinder() && + static_cast( baseControl )->Editor().TextView() ) + { + TInt componentIndex = iBody->ComponentIndexFromId( baseControl->ControlId() ); + baseControl->SetupAutomaticFindAfterFocusChangeL( iBody->CurrentFocus() <= componentIndex ); + } + + break; + } + case MCoeControlObserver::EEventRequestFocus: + { + // These things are done when pointer event is handled and focus should be changed. + // NOTE: CMsgAddressControlEditor is relying on focus not to set before + // pointer event is processed. Otherwise it is impossible to + // determine whether control has been focused before or not. + CMsgBaseControl* baseControl = static_cast( aControl ); + SetFocusByControlIdL( baseControl->ControlId(), ETrue, EFalse ); + + UpdateScrollBarL(); + } + default: + { + break; + } + } + } + //MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::HandleControlEventL")); + } +#else +void CMsgEditorView::HandleControlEventL( CCoeControl* /*aControl*/, TCoeEvent /*aEventType*/ ) + { + //MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::HandleControlEventL")); + } +#endif // RD_SCALABLE_UI_V2 + +// --------------------------------------------------------- +// CMsgEditorView::CreateHeaderL +// +// Creates the header. +// --------------------------------------------------------- +// +void CMsgEditorView::CreateHeaderL() + { + __ASSERT_DEBUG( iHeader == NULL, Panic( EMsgHeaderAlreadyExists ) ); + + TMargins margins; + + margins.iLeft = 0; + margins.iRight = 0; + margins.iTop = 0; + margins.iBottom = 0; + + iHeader = CMsgHeader::NewL( *this, margins ); + iHeader->SetObserver( this ); + } + +// --------------------------------------------------------- +// CMsgEditorView::CreateBodyL +// +// Creates the body. Creates also the default body control if requested. +// --------------------------------------------------------- +// +void CMsgEditorView::CreateBodyL() + { + __ASSERT_DEBUG( iBody == NULL, Panic( EMsgBodyAlreadyExists ) ); + + TMargins margins; + + margins.iLeft = 0; + margins.iRight = 0; + margins.iTop = 0; + margins.iBottom = 0; + + iBody = CMsgBody::NewL( *this, margins ); + iBody->SetObserver( this ); + + if ( !( iEditorModeFlags & EMsgDoNotUseDefaultBodyControl ) ) + { + CMsgBaseControl* bodyControl = CMsgBodyControl::NewL( this ); + bodyControl->SetControlType( EMsgBodyControl ); + DoAddControlL( bodyControl, EMsgComponentIdBody, EMsgFirstControl, EMsgBody ); + } + } + +// --------------------------------------------------------- +// CMsgEditorView::CreateScrollBarL +// +// Creates the scroll bar. +// --------------------------------------------------------- +// +void CMsgEditorView::CreateScrollBarL() + { + __ASSERT_DEBUG( iScrollBar == NULL, Panic( EMsgScrollBarAlreadyExists ) ); + + iScrollBar = new ( ELeave ) CEikScrollBarFrame( + this, // CCoeControl* aParentWindow + this // MEikScrollBarObserver* aObserver + ); + + // Check which type of scrollbar is to be shown + if ( AknLayoutUtils::DefaultScrollBarType( iAvkonAppUi ) == CEikScrollBarFrame::EDoubleSpan ) + { + // For EDoubleSpan type scrollbar + // non-window owning scrollbar + iScrollBar->CreateDoubleSpanScrollBarsL( EFalse, EFalse, ETrue, EFalse ); + iScrollBar->SetTypeOfVScrollBar( CEikScrollBarFrame::EDoubleSpan ); + + AknLayoutUtils::LayoutVerticalScrollBar( + iScrollBar, + TRect( TPoint( 0, 0 ), + MsgEditorCommons::MsgMainPane().Size() ), + AknLayoutScalable_Apps::scroll_pane_cp017().LayoutLine() ); + } + else + { + // For EArrowHead type scrollbar + iScrollBar->SetTypeOfVScrollBar( CEikScrollBarFrame::EArrowHead ); + } + + iScrollBar->SetScrollBarVisibilityL( CEikScrollBarFrame::EOff, + CEikScrollBarFrame::EAuto ); + } + +// --------------------------------------------------------- +// CMsgEditorView::UpdateScrollBarL +// +// Updates the scroll bar. +// --------------------------------------------------------- +// +void CMsgEditorView::UpdateScrollBarL() + { + TInt height( 0 ); + TInt pos( 0 ); + TInt windowSize( iViewRect.Height() - iBaseLineOffset ); + + // Round height up to the next full line height. + MsgEditorCommons::RoundToNextLine( windowSize, iLineHeight ); + + GetVirtualFormHeightAndPos( height, pos ); + + // Round height up to the next full line height. + MsgEditorCommons::RoundToNextLine( height, iLineHeight ); + + if ( pos < iLineHeight ) + { + pos = 0; + } + + // Round position up to the next full line height. + MsgEditorCommons::RoundToNextLine( pos, iLineHeight ); + +#ifdef _DEBUG + // CAknScrollIndicator::SetPosition has an __ASSERT_DEBUG + // for range check even if the control handles out-of-range + // values properly. + if ( pos > height ) pos = height; + if ( pos < -1 ) pos = -1; + +#endif + + if ( iScrollBar->TypeOfVScrollBar() == CEikScrollBarFrame::EDoubleSpan ) + { + TAknDoubleSpanScrollBarModel vDsSbarModel; + vDsSbarModel.SetScrollSpan( height ); + vDsSbarModel.SetWindowSize( windowSize ); + + pos = Max( 0, Min( height - windowSize, pos ) ); + + vDsSbarModel.SetFocusPosition( pos ); + + TEikScrollBarFrameLayout layout; + layout.iTilingMode = TEikScrollBarFrameLayout::EInclusiveRectConstant; + + // It seems to be important that we have separate + // variable for "inclusiveRect" and "clientRect" + TRect inclusiveRect( Rect() ); + TRect clientRect( Rect() ); + + iScrollBar->TileL( NULL, &vDsSbarModel, clientRect, inclusiveRect, layout ); + iScrollBar->SetVFocusPosToThumbPos( vDsSbarModel.FocusPosition() ); + + iViewFocusPosition = vDsSbarModel.FocusPosition(); + } + else + { + TEikScrollBarModel vertModel; + vertModel.iScrollSpan = height; + vertModel.iThumbSpan = windowSize; + vertModel.iThumbPosition = pos; + + TEikScrollBarFrameLayout layout; + + TRect rect( Rect() ); + iScrollBar->TileL( NULL, &vertModel, rect, rect, layout ); + iScrollBar->SetVFocusPosToThumbPos( vertModel.iThumbPosition ); + + iViewFocusPosition = vertModel.iThumbPosition; + } + + MsgEditorCommons::RoundToNextLine( iViewFocusPosition, iLineHeight ); + } + + +// --------------------------------------------------------- +// CMsgEditorView::GetVirtualFormHeightAndPos +// +// Return virtual height and virtual Y position of the form. +// This function assumes that only body can contain controls which virtual +// height must be calculated. Also this funtion assumes that only the +// currently focused control in the body can contain text above visible +// text view. +// --------------------------------------------------------- +// +void CMsgEditorView::GetVirtualFormHeightAndPos( TInt& aHeight, TInt& aPos ) + { + TInt screenHeight = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset; + + TInt scrollPartsHeight( 0 ); + TInt abovePartHeight( 0 ); + + TInt bodyHeight( iBody->VirtualHeight() ); + + TInt headerHeight( 0 ); + + TBool headerLoaded( iHeader->CountMsgControls() ); + if ( headerLoaded ) + { + // Header loaded so get header height and subtract base line offset + // from it as header first control contains it and it is not scrollable + // area. + headerHeight = iHeader->VirtualHeight() - iBaseLineOffset; + } + else + { + // No header so baseline offset is added to the first body control's distance. + // Baseline offset is not scrollable area so it must be subtracted from body height. + bodyHeight -= iBaseLineOffset; + } + + iVisiblePartHeight = bodyHeight + headerHeight; + + // Visible height is always at least one screen height. + iVisiblePartHeight = Max( screenHeight, iVisiblePartHeight ); + + // Calculate total scroll parts high excluding visible part and + // height of the scroll parts above visible part. + TInt singlePartHeight = Max( screenHeight, iVisiblePartHeight / 10 ); + for ( TInt current = 0; current < iScrollParts; current++ ) + { + if ( current != iVisiblePart ) + { + scrollPartsHeight += singlePartHeight; + + if ( current < iVisiblePart ) + { + abovePartHeight = scrollPartsHeight; + } + } + } + + aHeight = iVisiblePartHeight + scrollPartsHeight; + + if ( iCurrentFocus == EMsgBodyFocused ) + { + TInt bodyExt = abovePartHeight + iBody->VirtualExtension(); + + if ( headerLoaded ) + { + // Remove visible header pixels from header height. + TInt visiblePixels( iHeader->Position().iY + iHeader->Size().iHeight - iBaseLineOffset ); + if ( visiblePixels > 0 ) + { + headerHeight -= visiblePixels; + } + } + else + { + // No header so baseline offset is added to the first body control's distance. + // Baseline offset is not scrollable area so it must be subtracted from body extension. + bodyExt -= iBaseLineOffset; + } + + aPos = headerHeight + bodyExt; + + if ( iVisiblePart == ( iScrollParts - 1 ) ) + { + // This ensures that if we at the end of the scroll area then scroll position is also + // at the end. + CMsgBaseControl* ctrl = FocusedControl(); + + if ( ctrl && + ctrl->IsCursorLocation( EMsgBottom ) && + NextFocusableFormControl( iBody, + iBody->CurrentFocus() + 1, + EMsgFocusDown ) == KErrNotFound ) + { + // Further scrolling (down) not possible. + aPos = aHeight; + + if ( iScrollBar->TypeOfVScrollBar() == CEikScrollBarFrame::EDoubleSpan ) + { + // Focus position is top of the view. + aPos -= screenHeight; + } + } + } + } + else + { + aPos = abovePartHeight + iHeader->VirtualExtension() - iBaseLineOffset; + } + } + +// --------------------------------------------------------- +// CMsgEditorView::SetAndGetSizeL +// +// Sets sizes for the header and the body and returns their total size aSize +// as a reference. The function does not set new sizes for the controls if +// the aInit argument is EFalse. If aInit == ETrue, this function sets the size +// so that only a total height can change. +// --------------------------------------------------------- +// +void CMsgEditorView::SetAndGetSizeL( TSize& aSize, TBool aInit ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + if ( aInit && iStateFlags & EMsgStateInitialized ) + { + // Size changing at run time. Set intializing flag on. + iStateFlags |= EMsgStateInitializing; + } + + TSize editorSize( aSize ); + TSize headerSize( 0, 0 ); + TSize bodySize( 0, 0 ); + + headerSize = aSize; + iHeader->SetAndGetSizeL( headerSize, aInit ); + + bodySize = aSize; + iBody->SetAndGetSizeL( bodySize, aInit ); + + editorSize.iHeight = headerSize.iHeight + bodySize.iHeight; + + // View size must always be the size of client rect. Don't change "iSize"! + aSize = editorSize; + + if ( aInit && iStateFlags & EMsgStateInitialized ) + { + iStateFlags &= ~EMsgStateInitializing; + } + } + +// --------------------------------------------------------- +// CMsgEditorView::RefreshViewL +// +// Sets the positions for the controls and re-draws the view. +// --------------------------------------------------------- +// +void CMsgEditorView::RefreshViewL() + { + iStateFlags |= EMsgStateRefreshing; + + // Component must know their correct height by now. + AdjustComponentDistances(); + + TSize editorSize( iViewRect.Size() ); + + // Called after AdjustComponentDistances because that + // information is used on some components to calculate + // their sizes + SetAndGetSizeL( editorSize, ETrue ); + SizeChanged(); + + UpdateScrollBarL(); + + NotifyControlsForEvent( EMsgViewEventPrepareForViewing, 0 ); + DrawDeferred(); + + iStateFlags &= ~EMsgStateRefreshing; + + EnsureCorrectFormPosition( EFalse ); + } + +// --------------------------------------------------------- +// CMsgEditorView::NotifyControlsForEvent +// +// Notifies controls for an event. +// --------------------------------------------------------- +// +void CMsgEditorView::NotifyControlsForEvent( TMsgViewEvent aEvent, TInt aParam ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + iHeader->NotifyControlsForEvent( aEvent, aParam ); + iBody->NotifyControlsForEvent( aEvent, aParam ); + } + +// --------------------------------------------------------- +// CMsgEditorView::SetFocusByControlIdL +// +// Sets focus to a control aControlId. aSetCursorPos specified +// whether cursor position should be set or not. +// --------------------------------------------------------- +// +void CMsgEditorView::SetFocusByControlIdL( TInt aControlId, + TBool aCorrectFormPosition, + TBool aSetCursorPos ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + __ASSERT_DEBUG( aControlId >= 0, Panic( EMsgIncorrectControlId ) ); + + NotifyControlsForEvent( EMsgViewEventFocusMoveStarting, 0 ); + + ReportFocusMovement( MMsgEditorObserver::EMsgFocusMovingFrom ); + + TInt componentIndex = iHeader->ComponentIndexFromId( aControlId ); + + if ( componentIndex != KErrNotFound ) + { + // Control where focus is wanted to be moved was found from header. + if ( iCurrentFocus == EMsgBodyFocused ) + { + if (IsReadOnly()) // viewer + { + iBody->NotifyControlsForEvent( EMsgViewEventPrepareFocusTransitionUp, 0 ); + iBody->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + } + else + { + iBody->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + iBody->NotifyControlsForEvent( EMsgViewEventPrepareFocusTransitionUp, 0 ); + } + } + + TMsgFocus previousMsgPart = iCurrentFocus; + iCurrentFocus = EMsgHeaderFocused; + + TInt delta = componentIndex - iHeader->CurrentFocus(); + + if ( delta != 0 ) + { + iHeader->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + iHeader->NotifyControlsForEvent( delta > 0 ? EMsgViewEventPrepareFocusTransitionDown : + EMsgViewEventPrepareFocusTransitionUp, + 0 ); + } + + // Do real focus movement when focus is moved between + // message parts or when focus is moved inside message part + // to different component. Do it also if currently focused + // control does not have focus really. + if ( componentIndex != iHeader->CurrentFocus() || + previousMsgPart != iCurrentFocus || + !iHeader->FocusedControl()->IsFocused() ) + { + if ( !iHeader->ChangeFocusTo( componentIndex, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ) ) + { + __ASSERT_DEBUG( EFalse, Panic( EMsgNonFocusingControl ) ); + } + } + } + else + { + componentIndex = iBody->ComponentIndexFromId( aControlId ); + + if ( componentIndex != KErrNotFound ) + { + // Control where focus is wanted to be moved was found from body. + if ( iCurrentFocus == EMsgHeaderFocused ) + { + if (IsReadOnly()) + { + iHeader->NotifyControlsForEvent( EMsgViewEventPrepareFocusTransitionDown, 0 ); + iHeader->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + } + else + { + + iHeader->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + iHeader->NotifyControlsForEvent( EMsgViewEventPrepareFocusTransitionDown, 0 ); + } + } + TMsgFocus previousMsgPart = iCurrentFocus; + iCurrentFocus = EMsgBodyFocused; + + TInt delta = componentIndex - iBody->CurrentFocus(); + + if ( delta != 0 ) + { + iBody->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + iBody->NotifyControlsForEvent( delta ? EMsgViewEventPrepareFocusTransitionDown : + EMsgViewEventPrepareFocusTransitionUp, + 0 ); + } + + // Do real focus movement when focus is moved between + // message parts or when focus is moved inside message part + // to different component. Do it also if currently focused + // control does not have focus really. + if ( componentIndex != iBody->CurrentFocus() || + previousMsgPart != iCurrentFocus || + !iBody->FocusedControl()->IsFocused() ) + { + CMsgBaseControl* ctrl = iBody->MsgControl( componentIndex ); + + if ( aSetCursorPos && + ctrl && + ( ctrl->ControlType() == EMsgBodyControl || + ctrl->ControlType() == EMsgXhtmlBodyControl ) && + ctrl->ItemFinder() && + static_cast( ctrl )->Editor().TextView() ) + { + ctrl->SetupAutomaticFindAfterFocusChangeL( iBody->CurrentFocus() <= componentIndex ); + } + + if ( !iBody->ChangeFocusTo( componentIndex, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ) ) + { + __ASSERT_DEBUG( EFalse, Panic( EMsgNonFocusingControl ) ); + } + } + } + else + { + __ASSERT_DEBUG( EFalse, Panic( EMsgNoFocusingControlExist ) ); + } + } + + NotifyControlsForEvent( EMsgViewEventFocusMoveFinished, 0 ); + + ReportFocusMovement( MMsgEditorObserver::EMsgFocusMovedTo ); + + if ( aCorrectFormPosition ) + { + EnsureCorrectFormPosition( IsReadOnly() ); + } + + if ( aSetCursorPos && + IsReadOnly() && + FocusedControl() ) + { + FocusedControl()->NotifyViewEvent( EMsgViewEventSetCursorLastPos, 0 ); + } + } + +// --------------------------------------------------------- +// CMsgEditorView::DoAddControlL +// +// Does an actual add operation for the control by setting all the necessary +// observers etc. +// --------------------------------------------------------- +// +void CMsgEditorView::DoAddControlL( CMsgBaseControl* aControl, + TInt aControlId, + TInt aIndex, + TMsgFormComponent aFormComponent ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + // set readonly for control here because readonly flag + // may be needed before ActivateL call. + TUint32 flags = aControl->ControlModeFlags(); + if ( IsReadOnly() || + flags & EMsgControlModeReadOnly ) + { + flags |= EMsgControlModeReadOnly; + } + else + { + flags &= ~EMsgControlModeReadOnly; + } + aControl->SetControlModeFlags( flags ); + + if ( !aControl->OwnsWindow() ) + { + aControl->SetContainerWindowL( *this ); + } + + aControl->SetObserver( this ); + aControl->SetBaseControlObserver( *this ); + + if ( iEdwinObserver ) + { + CEikRichTextEditor* editor = NULL; + + if ( aControl->ControlType() == EMsgExpandableControl || + aControl->ControlType() == EMsgAddressControl ) + { + editor = &static_cast( aControl )->Editor(); + } + else if ( aControl->ControlType() == EMsgBodyControl || + aControl->ControlType() == EMsgXhtmlBodyControl ) + { + editor = &static_cast( aControl )->Editor(); + } + + if ( editor ) + { + editor->AddEdwinObserverL( iEdwinObserver ); + } + } + + aControl->MakeVisible( ETrue ); + aControl->ActivateL(); + + // this calls PrepareForReadOnly and sets flags again + // but it doesn't matter. + aControl->SetReadOnly( flags & EMsgControlModeReadOnly ); + + if ( aFormComponent == EMsgHeader ) + { + iHeader->AddControlL( aControl, aControlId, aIndex ); + } + else if ( aFormComponent == EMsgBody ) + { + iBody->AddControlL( aControl, aControlId, aIndex ); + + if ( iHeader->CountMsgControls() == 0 ) + { + iCurrentFocus = EMsgBodyFocused; + } + } + else + { + __ASSERT_DEBUG( EFalse, Panic( EMsgIncorrectFormComponent ) ); + } + + if ( aControl->UniqueHandle() == KErrNotFound ) + { + // Control added first time. Set unique handle. + aControl->SetUniqueHandle( iUniqueHandlePool ); + iUniqueHandlePool++; + } + + if ( iStateFlags & EMsgStateInitialized ) // Control is added on run-time. + { + TRAPD( err, RefreshViewL() ); + + if ( err != KErrNone ) + { + // if RefreshViewL fails, remove the added control from internal + // structures, i.e. transfer ownership back to caller. + if ( aFormComponent == EMsgHeader ) + { + iHeader->RemoveControlL( aControlId ); // does not leave. + } + else if ( aFormComponent == EMsgBody ) + { + iBody->RemoveControlL( aControlId ); // does not leave. + } + + User::Leave( err ); + } + else + { + // Set control initialized as it has been successfully added & activated. + flags = aControl->ControlModeFlags(); + flags |= EMsgControlModeInitialized; + aControl->SetControlModeFlags( flags ); + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::RotateFocusL +// +// Rotates focus up or down depending aDirection argument. Returns ETrue if +// operation can be done. +// --------------------------------------------------------- +// +TBool CMsgEditorView::RotateFocusL( TMsgFocusDirection aDirection, + MMsgEditorObserver::TMsgFocusEvent& aFocusEvent ) + { + __ASSERT_DEBUG( iHeader != NULL, Panic( EMsgHeaderNotExists ) ); + __ASSERT_DEBUG( iBody != NULL, Panic( EMsgBodyNotExists ) ); + + TBool startAutoFind = EFalse; + TBool setMaxHeight = EFalse; + + TInt newFocus = KErrNotFound; + TInt controlIndex = 0; + + CMsgFormComponent* currForm = NULL; + CMsgFormComponent* nextForm = NULL; + + if ( iCurrentFocus == EMsgHeaderFocused ) + { + currForm = iHeader; + controlIndex = iHeader->CurrentFocus(); + + if ( aDirection == EMsgFocusDown ) + { + newFocus = NextFocusableFormControl( iHeader, controlIndex + 1, EMsgFocusDown ); + + if ( newFocus != KErrNotFound ) + { + nextForm = iHeader; + } + else + { + newFocus = NextFocusableFormControl( iBody, 0, EMsgFocusDown ); + + if ( newFocus != KErrNotFound ) + { + startAutoFind = ETrue; + nextForm = iBody; + aFocusEvent = MMsgEditorObserver::EMsgFocusToBody; + } + else + { + TInt currentlyFocused = iBody->CurrentFocus(); + + if ( IsFocusable( iBody->MsgControl( currentlyFocused ), EMsgFocusDown ) ) + { + // Refocus the currently focused control. + nextForm = iBody; + startAutoFind = ETrue; + newFocus = currentlyFocused; + } + + aFocusEvent = MMsgEditorObserver::EMsgFocusAtBottom; + } + } + } + else if ( aDirection == EMsgFocusUp ) + { + newFocus = NextFocusableFormControl( iHeader, controlIndex - 1, EMsgFocusUp ); + + if ( newFocus != KErrNotFound ) + { + nextForm = iHeader; + } + else + { + aFocusEvent = MMsgEditorObserver::EMsgFocusAtTop; + } + } + } + else if ( iCurrentFocus == EMsgBodyFocused ) + { + currForm = iBody; + controlIndex = iBody->CurrentFocus(); + + if ( aDirection == EMsgFocusDown ) + { + newFocus = NextFocusableFormControl( iBody, controlIndex + 1, EMsgFocusDown ); + + if ( newFocus != KErrNotFound ) + { + nextForm = iBody; + } + else + { + aFocusEvent = MMsgEditorObserver::EMsgFocusAtBottom; + } + } + else if ( aDirection == EMsgFocusUp ) + { + newFocus = NextFocusableFormControl( iBody, controlIndex - 1, EMsgFocusUp ); + + if ( newFocus != KErrNotFound ) + { + nextForm = iBody; + } + else + { + newFocus = NextFocusableFormControl( iHeader, iHeader->CountMsgControls() - 1, EMsgFocusUp ); + setMaxHeight = ETrue; + + if ( newFocus != KErrNotFound ) + { + nextForm = iHeader; + aFocusEvent = MMsgEditorObserver::EMsgFocusToHeader; + } + else + { + TInt currentlyFocused = iHeader->CurrentFocus(); + if ( IsFocusable( iHeader->MsgControl( currentlyFocused ), EMsgFocusUp ) ) + { + // Refocus the currently focused control. + nextForm = iHeader; + newFocus = currentlyFocused; + } + + aFocusEvent = MMsgEditorObserver::EMsgFocusAtTop; + } + } + } + + if ( ( aFocusEvent == MMsgEditorObserver::EMsgFocusNone || + aFocusEvent == MMsgEditorObserver::EMsgFocusToBody ) && + ( newFocus != controlIndex && + newFocus > KErrNotFound ) ) // for automatic highlight + { + // we assume that editor size is correct in this point + startAutoFind = ETrue; + } + + } + + if ( setMaxHeight ) + { + CMsgBaseControl* ctrl = FocusedControl(); // for automatic highlight + + if ( ctrl && + ( ctrl->ControlType() == EMsgBodyControl || + ctrl->ControlType() == EMsgXhtmlBodyControl ) && + ctrl->ItemFinder() ) + { + static_cast( ctrl )->Editor().SetMaximumHeight( + MsgEditorCommons::MaxBodyHeight() ); + } // for automatic highlight + } + + if ( nextForm != NULL ) + { + NotifyControlsForEvent( aDirection == EMsgFocusUp ? EMsgViewEventPrepareFocusTransitionUp : + EMsgViewEventPrepareFocusTransitionDown, + 0 ); + + NotifyControlsForEvent( EMsgViewEventFocusMoveStarting, 0 ); + + ReportFocusMovement( MMsgEditorObserver::EMsgFocusMovingFrom ); + + if ( nextForm == currForm ) + { + currForm->ChangeFocusTo( newFocus, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + } + else + { + currForm->SetFocus( EFalse, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + iCurrentFocus = ( nextForm == iBody ) ? EMsgBodyFocused : + EMsgHeaderFocused; + + nextForm->ChangeFocusTo( newFocus, + iStateFlags & EMsgStateInitialized ? EDrawNow : + ENoDrawNow ); + } + + CMsgBaseControl* focusedControl = FocusedControl(); + + if ( focusedControl ) + { + if ( aDirection == EMsgFocusDown ) + { + focusedControl->NotifyViewEvent( EMsgViewEventSetCursorFirstPos, 0 ); + } + else if ( aDirection == EMsgFocusUp ) + { + focusedControl->NotifyViewEvent( EMsgViewEventSetCursorLastPos, 0 ); + } + } + + NotifyControlsForEvent( EMsgViewEventFocusMoveFinished, 0 ); + + ReportFocusMovement( MMsgEditorObserver::EMsgFocusMovedTo ); + } + else + { + CMsgBaseControl* focusedControl = FocusedControl(); + + if ( !IsReadOnly() && focusedControl && !startAutoFind ) + { + // Focus is tried to move to direction that does not cause any focus + // changes (either up when already at the topmost position or down when + // already at the bottommost position) on editor. + // => Set focused cursor position to either last or first position if we + // are at last or first scroll part. + if ( aDirection == EMsgFocusDown && + iVisiblePart == iScrollParts - 1 ) + { + if ( focusedControl->ControlType() == EMsgBodyControl || + focusedControl->ControlType() == EMsgXhtmlBodyControl ) + { + CEikRichTextEditor& editor = static_cast( focusedControl )->Editor(); + editor.SetCursorPosL( editor.TextLength(), EFalse ); + } + else if ( focusedControl->ControlType() == EMsgExpandableControl || + focusedControl->ControlType() == EMsgAddressControl ) + { + CEikRichTextEditor& editor = + static_cast( focusedControl )->Editor(); + editor.SetCursorPosL( editor.TextLength(), EFalse ); + + if ( focusedControl->ControlType() == EMsgAddressControl ) + { + // Update the address field highlighting + static_cast( editor ).CheckHighlightingL(); + } + } + } + else if ( aDirection == EMsgFocusUp && + iVisiblePart == 0) + { + if ( focusedControl->ControlType() == EMsgBodyControl || + focusedControl->ControlType() == EMsgXhtmlBodyControl ) + { + CEikRichTextEditor& editor = static_cast( focusedControl )->Editor(); + editor.SetCursorPosL( 0, EFalse ); + } + else if ( focusedControl->ControlType() == EMsgExpandableControl || + focusedControl->ControlType() == EMsgAddressControl ) + { + CEikRichTextEditor& editor = + static_cast( focusedControl )->Editor(); + editor.SetCursorPosL( 0, EFalse ); + + if ( focusedControl->ControlType() == EMsgAddressControl ) + { + // Update the address field highlighting + static_cast( editor ).CheckHighlightingL(); + } + } + } + } + } + + if ( startAutoFind ) + { + CMsgBaseControl* ctrl = (CMsgBaseControl*)iBody->MsgControl( newFocus ); + + if ( ctrl && + ( ctrl->ControlType() == EMsgBodyControl || + ctrl->ControlType() == EMsgXhtmlBodyControl ) && + ctrl->ItemFinder() && + static_cast( ctrl )->Editor().TextView() ) + { + ctrl->SetupAutomaticFindAfterFocusChangeL( aDirection == EMsgFocusDown ); + } + } + + return newFocus != KErrNotFound; + } + +// --------------------------------------------------------- +// CMsgEditorView::EnsureCorrectFormPosition +// +// Ensures that the cursor is visible on the view and that the form position +// is correct. +// --------------------------------------------------------- +// +TBool CMsgEditorView::EnsureCorrectFormPosition( TBool /*aScrollDown*/, TBool /*aScrollUp*/ ) + { + CMsgBaseControl* ctrl = FocusedControl(); + if ( !ctrl ) + { + return EFalse; + } + + iStateFlags |= EMsgEnsureCorrectFormPositionRequestIssued; + + TInt delta = 0; + TBool viewScrolled = ETrue; + TRect lineRect = ctrl->IsReadOnly() ? ctrl->Rect() : + ctrl->CurrentLineRect(); + + if ( lineRect.Height() ) + { + if ( lineRect.iBr.iY > iViewRect.Height() ) + { + // the view must be scrolled up. + delta = iViewRect.Height() - lineRect.iBr.iY; + } + else if ( lineRect.iTl.iY < 0 && + lineRect.Height() <= iViewRect.Height() ) + { + // the view must be scrolled down. + delta = -lineRect.iTl.iY; + } + + // Adjust to next(/previous) baseline + if ( delta > 0 ) + { + MsgEditorCommons::RoundToNextLine( delta, iLineHeight ); + } + else + { + MsgEditorCommons::RoundToPreviousLine( delta, iLineHeight ); + } + } + + if ( delta ) + { + EnsureCorrectCursorPosition(); + + viewScrolled = EFalse; + ScrollForm( delta, ETrue ); + } + + iStateFlags &= ~EMsgEnsureCorrectFormPositionRequestIssued; + + return viewScrolled; + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleHeightChangedL +// +// Handles the form's height change. Gets pointer aControl to a control which +// height is changing. If aDeltaHeight > 0 the height is increasing. +// Assumes that control's size has already been changed. +// --------------------------------------------------------- +// +TBool CMsgEditorView::HandleHeightChangedL( CMsgBaseControl* /*aControl*/, + TInt /*aDeltaHeight*/ ) + { + MEBLOGGER_ENTERFN( "CMsgEditorView::HandleHeightChangedL" ); + + TSize headerSize = iHeader->Size(); + + TSize size( headerSize + iBody->Size() ); + TSize thisSize( iViewRect.Size() ); + + SetAndGetSizeL( thisSize, EFalse ); + + if ( size.iHeight != thisSize.iHeight ) + { + SizeChanged(); + } + + if ( ViewInitialized() ) + { + if ( headerSize != iHeader->Size() ) + { + DrawNow(); + } + else + { + DrawDeferred(); + } + } + + MEBLOGGER_ENTERFN( "CMsgEditorView::HandleHeightChangedL" ); + + return ETrue; + } + +// --------------------------------------------------------- +// CMsgEditorView::ScrollForm +// +// Scrolls the form up or down depending on aDelta. If aDelta > 0, the form +// scrolls down. Form offset should also be less or equal to 0. +// --------------------------------------------------------- +// +TBool CMsgEditorView::ScrollForm( TInt aDelta, TBool aUpdateScrollBarPos ) + { + TBool result( ETrue ); + + if ( iFormOffset + aDelta > 0 ) + { + // Scrolling form given amount of pixels would result form to go above + // it's uppermost position. + aDelta = -iFormOffset; + + result = EFalse; + } + + if ( aDelta != 0 ) + { + iFormOffset += aDelta; + + // Real scrolling is performed here. + SizeChanged(); + + // Do not redraw the screen if we are in the middle of state refreshing. + if ( !( iStateFlags & EMsgStateRefreshing ) ) + { + DrawNow(); + } + + NotifyControlsForEvent( EMsgViewEventFormScrolled, aDelta ); + + if ( aUpdateScrollBarPos ) + { + iViewFocusPosition -= aDelta; + + const TAknDoubleSpanScrollBarModel* model = AknScrollBarModel(); + + if ( model ) + { + iScrollBar->MoveThumbsBy( 0, -aDelta ); + } + } + } + + return result; + } + +// --------------------------------------------------------- +// CMsgEditorView::SetViewRect +// +// Sets view rect. +// --------------------------------------------------------- +// +void CMsgEditorView::SetViewRect( const TRect& /*aRect*/ ) + { + iViewRect = MsgEditorCommons::MsgMainPane(); + + Window().SetExtent( iViewRect.iTl, iViewRect.Size() ); + } + +// --------------------------------------------------------- +// CMsgEditorView::AdjustComponentDistances +// +// Set proper distances between controls to obey LAF coordinates. +// +// Note: Components must know their correct height when this function +// is called. +// --------------------------------------------------------- +// +void CMsgEditorView::AdjustComponentDistances() + { + TInt i; + CMsgBaseControl* ctrl; + TInt controlType; + TInt countHeader = iHeader->CountMsgControls(); + TInt countBody = iBody->CountMsgControls(); + + TInt baseLine = iLineHeight; + TInt firstComponentOffset = iBaseLineOffset; + + // beforeOffset + control height + afterOffset + // must be multiple of baseLine! + TInt beforeOffset = 0; + TInt afterOffset = 0; + TInt distance = 0; + + for ( i = 0; i < countHeader; i++ ) + { + ctrl = iHeader->MsgControl( i ); + if ( ctrl ) + { + controlType = ctrl->ControlType(); + + if ( controlType == EMsgAddressControl || + controlType == EMsgExpandableControl || + controlType == EMsgAttachmentControl ) + { + distance = ( i == 0 ) ? firstComponentOffset : + 0; + distance += afterOffset; + + // At least in APAC there's offset in the header + // fields. A header field is 21 pixels high whereas + // the baseline is 19 pixels. + TInt offset = -( ctrl->Size().iHeight % baseLine ); + // TODO: Minor MAGIC + // header frames take 3 pixels so it's ok to + // have offset of "-3". + if ( offset < -3 ) + { + offset += baseLine; + } + // divide offset evenly between before and after offset + //beforeOffset = offset / 2; + //afterOffset = offset - beforeOffset; + //distance += beforeOffset; + distance += offset; + + ctrl->SetDistanceFromComponentAbove( distance ); + } + } + } + + for ( i = 0; i < countBody; i++ ) + { + ctrl = iBody->MsgControl( i ); + + if ( ctrl ) + { + if ( i == 0 ) + { + distance = ( countHeader == 0 ) ? firstComponentOffset : + 0; + } + else + { + distance = baseLine; + } + + distance += afterOffset; + + controlType = ctrl->ControlType(); + + switch ( controlType ) + { + case EMsgImageControl: + case EMsgAudioControl: + { + // Image control height is always + // multiple of baseLine + beforeOffset = 0; + afterOffset = 0; + break; + } + case EMsgVideoControl: + { + // Video must be precisely at the top of the screen. + beforeOffset = -firstComponentOffset; + // Video control height is not multiple of baseLine + // -> calculate offset + afterOffset = firstComponentOffset - + ( ctrl->Size().iHeight % baseLine ); + break; + } + default: + { + beforeOffset = 0; + afterOffset = 0; + break; + } + } + distance += beforeOffset; + + ctrl->SetDistanceFromComponentAbove( distance ); + + // TODO: Is this the correct place to check this?! + if ( !IsReadOnly() && + ( controlType == EMsgBodyControl || + controlType == EMsgXhtmlBodyControl ) ) + { + TUint32 flags = ctrl->ControlModeFlags(); + if ( iBody->ComponentIndexFromId( EMsgComponentIdBody ) == + countBody - 1 ) + { + // In editor mode bodycontrol must have max height + // if it's the last control in msg body even if it + // has no text so that all editor lines are drawn. + flags |= EMsgControlModeBodyMaxHeight; + } + else + { + flags &= ~EMsgControlModeBodyMaxHeight; + } + + ctrl->SetControlModeFlags( flags ); + } + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::SetComponentsInitialized +// +// Set all controls initialized. +// --------------------------------------------------------- +// +void CMsgEditorView::SetComponentsInitialized() + { + TInt i; + CMsgBaseControl* ctrl; + TInt countHeader = iHeader->CountMsgControls(); + TInt countBody = iBody->CountMsgControls(); + + TUint32 flags; + + for ( i = 0; i < countHeader; i++ ) + { + ctrl = iHeader->MsgControl( i ); + + if ( ctrl ) + { + flags = ctrl->ControlModeFlags(); + flags |= EMsgControlModeInitialized; + ctrl->SetControlModeFlags( flags ); + } + } + + for ( i = 0; i < countBody; i++ ) + { + ctrl = iBody->MsgControl( i ); + + if ( ctrl ) + { + flags = ctrl->ControlModeFlags(); + flags |= EMsgControlModeInitialized; + ctrl->SetControlModeFlags( flags ); + } + } + } + + +// --------------------------------------------------------- +// CMsgEditorView::SetAfterFocusL +// +// +// +// --------------------------------------------------------- +// +TBool CMsgEditorView::SetAfterFocusL( MMsgEditorObserver::TMsgAfterFocusEventFunc aAfterFocus ) + { + TBool forceScrollUp = EFalse; + CMsgBaseControl* ctrl; + TInt newFocus; + CMsgBodyControl* body = static_cast( ControlById( EMsgComponentIdBody ) ); + CEikRichTextEditor* bodyEditor = NULL; + + if ( body ) + { + bodyEditor = &body->Editor(); + body->SetupAutomaticFindAfterFocusChangeL( + aAfterFocus == MMsgEditorObserver::EMsgCursorToBodyBeginning ); + } + + if ( aAfterFocus == MMsgEditorObserver::EMsgCursorToBodyBeginning ) + { + newFocus = iBody->FirstFocusableControl( 0, EMsgFocusDown ); + + if ( newFocus != KErrNotFound ) + { + ctrl = iBody->MsgControl( newFocus ); + SetFocusByControlIdL( ctrl->ControlId() ); + + if ( bodyEditor ) + { + bodyEditor->SetCursorPosL( 0, EFalse ); + } + } + } + else if ( aAfterFocus == MMsgEditorObserver::EMsgCursorToBodyEnd ) + { + newFocus = iBody->FirstFocusableControl( iBody->CountMsgControls() - 1, EMsgFocusUp ); + + if ( newFocus == KErrNotFound ) + { + // empty body perhaps... + newFocus = iHeader->FirstFocusableControl( iHeader->CountMsgControls() - 1, EMsgFocusUp ); + + if ( newFocus != KErrNotFound ) + { + ctrl = iHeader->MsgControl( newFocus ); + SetFocusByControlIdL( ctrl->ControlId(), EFalse ); + } + } + else + { + ctrl = iBody->MsgControl( newFocus ); + SetFocusByControlIdL( ctrl->ControlId(), EFalse ); + + if ( bodyEditor ) + { + bodyEditor->SetCursorPosL( bodyEditor->TextLength(), EFalse ); + } + + forceScrollUp = ETrue; + } + } + return forceScrollUp; + } + +// --------------------------------------------------------- +// CMsgEditorView::ItemFinder +// +// --------------------------------------------------------- +// +EXPORT_C CItemFinder* CMsgEditorView::ItemFinder() + { + if ( iBody ) + { + CMsgBaseControl* ctrl; + TInt countBody = iBody->CountMsgControls(); + + for ( TInt i = 0; i < countBody; i++ ) + { + ctrl = iBody->MsgControl( i ); + if ( ctrl && + ( ctrl->ControlType() == EMsgBodyControl || + ctrl->ControlType() == EMsgXhtmlBodyControl ) ) + { + return ctrl->ItemFinder(); + } + } + } + return 0; + } + +// --------------------------------------------------------- +// CMsgEditorView::SetEdwinObserverL +// +// --------------------------------------------------------- +// +EXPORT_C void CMsgEditorView::SetEdwinObserverL( MEikEdwinObserver* aObserver ) + { + iEdwinObserver = aObserver; + + TInt headerControlCount = iHeader->CountMsgControls(); + TInt controlCount = headerControlCount + iBody->CountMsgControls(); + + for ( TInt controlIndex = 0; controlIndex < controlCount; controlIndex++ ) + { + CMsgBaseControl* control = NULL; + if ( controlIndex < headerControlCount ) + { + control = iHeader->MsgControl( controlIndex ); + } + else + { + control = iBody->MsgControl( controlIndex - headerControlCount ); + } + + CEikRichTextEditor* editor = NULL; + if ( control->ControlType() == EMsgExpandableControl || + control->ControlType() == EMsgAddressControl ) + { + editor = &static_cast( control )->Editor(); + } + else if ( control->ControlType() == EMsgBodyControl || + control->ControlType() == EMsgXhtmlBodyControl ) + { + editor = &static_cast( control )->Editor(); + } + + if ( editor ) + { + editor->RemoveEdwinObserver( iEdwinObserver ); + + if ( iEdwinObserver ) + { + editor->AddEdwinObserverL( iEdwinObserver ); + } + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::ControlFullyVisible +// +// If control's: +// - top Y-coordinate is below or equal messaging main pane top Y-coordinate and +// - bottom Y-coordinate is above or equal messaging main pane bottom Y-coordinate +// then control is fully visible. Currently all controls are expected +// to have equal width as messaging data pane. +// --------------------------------------------------------- +// +TBool CMsgEditorView::ControlFullyVisible( CMsgBaseControl* aControl ) const + { + TBool retVal = EFalse; + + if ( aControl ) + { + TRect rect = aControl->Rect(); + if ( rect.iTl.iY >= 0 && + rect.iBr.iY <= MsgEditorCommons::EditorViewHeigth() ) + { + retVal = ETrue; + } + } + + return retVal; + } + +// --------------------------------------------------------- +// CMsgEditorView::NextFocusableFormControl +// +// Searches then next focusable form control from given form component +// starting from certain control and proceeding into given direction. +// +// Stops when either focusable control is found or there is no more +// controls left. See CMsgEditorView::IsFocusable function for +// description when control is focusable. +// --------------------------------------------------------- +// +TInt CMsgEditorView::NextFocusableFormControl( CMsgFormComponent* aFormComponent, + TInt aStart, + TMsgFocusDirection aDirection ) + { + TInt newFocus = aFormComponent->FirstFocusableControl( aStart, aDirection ); + TBool found = EFalse; + + while ( newFocus != KErrNotFound && + !found ) + { + found = IsFocusable( aFormComponent->MsgControl( newFocus ), aDirection ); + + if ( !found ) + { + newFocus = aFormComponent->FirstFocusableControl( + aDirection == EMsgFocusDown ? newFocus + 1 : + newFocus - 1, + aDirection ); + } + } + + return newFocus; + } + +// --------------------------------------------------------- +// CMsgEditorView::MopSupplyObject +// +// --------------------------------------------------------- +// +EXPORT_C TTypeUid::Ptr CMsgEditorView::MopSupplyObject( TTypeUid aId ) + { + if ( aId.iUid == MAknsControlContext::ETypeId ) + { + return MAknsControlContext::SupplyMopObject( aId, iBgContext ); + } + + return CCoeControl::MopSupplyObject( aId ); + } + +// --------------------------------------------------------- +// CMsgEditorView::HandleScrollEventL +// +// --------------------------------------------------------- +// +#ifdef RD_SCALABLE_UI_V2 +void CMsgEditorView::HandleScrollEventL( CEikScrollBar* aScrollBar, + TEikScrollEvent aEventType ) + { + switch ( aEventType ) + { + case EEikScrollUp: + { + ScrollViewL( iLineHeight, EMsgScrollUp, ETrue ); + EnsureCorrectScrollPartL( AknScrollBarModel()->FocusPosition() ); + break; + } + case EEikScrollDown: + { + ScrollViewL( iLineHeight, EMsgScrollDown, ETrue ); + EnsureCorrectScrollPartL( AknScrollBarModel()->FocusPosition() ); + break; + } + case EEikScrollTop: + { + // Not supported yet. + break; + } + case EEikScrollBottom: + { + // Not supported yet. + break; + } + case EEikScrollThumbReleaseVert: + { + iScrollBar->DrawBackground( ETrue, ETrue ); + + EnsureCorrectScrollPartL( AknScrollBarModel()->FocusPosition() ); + iPopUpPart = -1; + break; + } + case EEikScrollPageUp: + case EEikScrollPageDown: + { + TInt scrolledPixels = iViewFocusPosition - AknScrollBarModel()->FocusPosition(); + + // Round to the previous full line. + MsgEditorCommons::RoundToPreviousLine( scrolledPixels, iLineHeight ); + + if ( scrolledPixels != 0 ) + { + ScrollViewL( Abs( scrolledPixels ), + scrolledPixels > 0 ? EMsgScrollUp : + EMsgScrollDown, + EFalse ); + + EnsureCorrectScrollPartL( AknScrollBarModel()->FocusPosition() ); + + iScrollBar->DrawScrollBarsNow(); + } + break; + } + case EEikScrollThumbDragVert: + { + TInt currentPart( CurrentScrollPart( AknScrollBarModel()->FocusPosition() ) ); + + if ( currentPart == iVisiblePart ) + { + if ( iPopUpPart != -1 ) + { + // Hide the popup if visible + static_cast( aScrollBar )->SetScrollPopupInfoTextL( KNullDesC ); + iPopUpPart = -1; + } + } + + if ( iPopUpPart == -1 ) + { + TInt scrolledPixels = iViewFocusPosition - AknScrollBarModel()->FocusPosition(); + + // Round to the previous full line. + MsgEditorCommons::RoundToPreviousLine( scrolledPixels, iLineHeight ); + + if ( scrolledPixels != 0 ) + { + ScrollViewL( Abs( scrolledPixels ), + scrolledPixels > 0 ? EMsgScrollUp : + EMsgScrollDown, + EFalse ); + } + } + + TInt pixelsScrolled = iViewFocusPosition - AknScrollBarModel()->FocusPosition(); + TMsgScrollDirection scrollDirection = pixelsScrolled > 0 ? EMsgScrollUp : EMsgScrollDown; + + // If scrolled position is outside of visible part, then there is no need to + // reset thumb position and no page number pop-up need be shown. + if ( ( currentPart != iVisiblePart ) && + EnsureVisiblePartScrollComplete ( AknScrollBarModel()->FocusPosition(), scrollDirection ) ) + { + // Thumb position is forced to the top most position of currently scrolled slide. + TInt currentHeight( 0 ); + TInt screenHeight = iViewRect.Height() - iBaseLineOffset; + + for ( TInt current = 0; current < currentPart; current++ ) + { + if ( current == iVisiblePart ) + { + currentHeight += iVisiblePartHeight; + } + else + { + currentHeight += screenHeight; + } + } + + iScrollBar->DrawBackground( ETrue, EFalse ); + iScrollBar->SetVFocusPosToThumbPos( currentHeight ); + + if ( currentPart != iPopUpPart ) + { + ShowScrollPopupInfoTextL( static_cast( aScrollBar ), + currentPart + 1 ); + iPopUpPart = currentPart; + } + } + else + { + iScrollBar->DrawScrollBarsNow(); + } + break; + } + default: + { + break; + } + } + } +#else +void CMsgEditorView::HandleScrollEventL( CEikScrollBar* /*aScrollBar*/, + TEikScrollEvent /*aEventType*/ ) + { + } +#endif // RD_SCALABLE_UI_V2 + +// --------------------------------------------------------- +// CMsgEditorView::Draw +// Draws skin background for the view area. With video control +// this should not be done since we might accidentally draw over +// the video frame. In this case we only draw around the video control +// and let the video control draw its own background when approriate. +// --------------------------------------------------------- +// +void CMsgEditorView::Draw( const TRect& aRect ) const + { + CWindowGc& gc = SystemGc(); + + CMsgBaseControl* videoControl = iBody->Component( EMsgComponentIdVideo ); + + TBool backgroundDrawn( EFalse ); + if ( !videoControl ) + { + backgroundDrawn = AknsDrawUtils::Background( + AknsUtils::SkinInstance(), + AknsDrawUtils::ControlContext( this ), + this, + gc, + aRect ); + } + else + { + backgroundDrawn = AknsDrawUtils::BackgroundBetweenRects( + AknsUtils::SkinInstance(), + AknsDrawUtils::ControlContext( this ), + this, + gc, + aRect, + videoControl->Rect() ); + } + + + if ( !backgroundDrawn ) + { + gc.SetBrushColor( AKN_LAF_COLOR( 0 ) ); + gc.SetBrushStyle( CGraphicsContext::ESolidBrush ); + gc.SetPenStyle( CGraphicsContext::ENullPen ); + gc.DrawRect( aRect ); + } + } + +// --------------------------------------------------------- +// CMsgEditorView::ReportFocusMovement +// +// --------------------------------------------------------- +// +void CMsgEditorView::ReportFocusMovement( TInt aFocusEvent ) + { + CMsgBaseControl* ctrl = FocusedControl(); + + TInt controlId = ctrl ? ctrl->ControlId() : + EMsgComponentIdNull; + + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgHandleFocusChange, + &aFocusEvent, + &controlId, + NULL ); + } + +// --------------------------------------------------------- +// CMsgEditorView::ControlFromPosition +// +// --------------------------------------------------------- +// +CMsgBaseControl* CMsgEditorView::ControlFromPosition( TPoint aPosition, + TBool aEvaluateHitTest ) const + { + CMsgBaseControl* control = NULL; + + if ( iHeader->Rect().Contains( aPosition ) ) + { + control = iHeader->ControlFromPosition( aPosition, aEvaluateHitTest ); + } + else + { + control = iBody->ControlFromPosition( aPosition, aEvaluateHitTest ); + } + + return control; + } + +// --------------------------------------------------------- +// CMsgEditorView::EnsureVisiblePartScrollComplete +// +// Description +// --------------------------------------------------------- + +TBool CMsgEditorView::EnsureVisiblePartScrollComplete( TInt aFocusPosition, + TMsgScrollDirection aDirection ) + { + MEBLOGGER_ENTERFN( "CMsgEditorView::EnsureVisiblePartScrollComplete" ); + TInt scrollTillPosition( 0 ); + TBool result = ETrue; + + TInt screenHeight = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset; + + for ( TInt current = 0; current < iVisiblePart ; current++ ) + scrollTillPosition += screenHeight; + + if ( aDirection == EMsgScrollDown ) + { + scrollTillPosition += iVisiblePartHeight; + + if ( aFocusPosition < scrollTillPosition ) + result = EFalse; + } + else + { + if ( aFocusPosition > scrollTillPosition ) + result = EFalse; + } + + MEBLOGGER_LEAVEFN( "CMsgEditorView::EnsureVisiblePartScrollComplete" ); + return result; + } + +// --------------------------------------------------------- +// CMsgEditorView::ScrollViewL +// +// First bound check is made so that no scrolling below minimum & +// above maximum is performed. This might happen with out check +// when using scrollbar arrows. DoScrollViewL function is called +// to perform the real scrolling. After all scrolling is done we +// move the thumb position if this is requested by the caller. +// --------------------------------------------------------- +// +void CMsgEditorView::ScrollViewL( TInt aPixelsToScroll, + TMsgScrollDirection aDirection, + TBool aMoveThumb ) + { + MEBLOGGER_ENTERFN( "CMsgEditorView::ScrollViewL" ); + + const TAknDoubleSpanScrollBarModel* model = AknScrollBarModel(); + + // Maximum value for thumb [0..scrollSpan] + TInt scrollSpan = model->ScrollSpan(); + + // Position of the topmost part of the thumb + TInt thumbTopPos = iViewFocusPosition; + + // Position of the downmost part of the thumb + TInt thumbDownPos = thumbTopPos + model->WindowSize(); + + // Bound check + if ( ( aDirection == EMsgScrollUp && thumbTopPos <= 0 ) || + ( aDirection == EMsgScrollDown && thumbDownPos >= scrollSpan ) ) + { + return; + } + + if ( CurrentScrollPart( iViewFocusPosition ) != iVisiblePart ) + { + // No need to perform scrolling, if the visible part is completely + // scrolled and the new scroll position is outside the visible part + if ( EnsureVisiblePartScrollComplete ( iViewFocusPosition, aDirection ) ) + return; + } + + DoScrollViewL( aPixelsToScroll, aDirection ); + + if ( aMoveThumb ) + { + // Scroll bar thumb is moved only if caller requests it. Scroll bar seems to + // add/subtract 1 pixel depending of direction. + TInt directionMultiplier( -1 ); + if ( aDirection == EMsgScrollDown ) + { + directionMultiplier = 1; + } + + iScrollBar->SetVFocusPosToThumbPos( model->FocusPosition() - + directionMultiplier * 1 + + directionMultiplier * aPixelsToScroll ); + } + + MEBLOGGER_LEAVEFN( "CMsgEditorView::ScrollViewL" ); + } + +// --------------------------------------------------------- +// CMsgEditorView::EnsureCorrectCursorPosition +// +// Ensures that cursor position relative to focus position +// on all controls is on correct position. +// --------------------------------------------------------- +// +void CMsgEditorView::EnsureCorrectCursorPosition() + { + CMsgFormComponent* currentForm = NULL; + if ( iCurrentFocus == EMsgHeaderFocused ) + { + iBody->NotifyControlsForEvent( EMsgViewEventSetCursorFirstPos, 0 ); + currentForm = iHeader; + } + else + { + iHeader->NotifyControlsForEvent( EMsgViewEventSetCursorLastPos, 0 ); + currentForm = iBody; + } + + TInt componentIndex = currentForm->ComponentIndexFromId( currentForm->FocusedControl()->ControlId() ); + + for ( TInt current = 0; current < currentForm->CountMsgControls(); current++ ) + { + CMsgBaseControl* control = currentForm->MsgControl( current ); + TInt currentIndex = currentForm->ComponentIndexFromId( control->ControlId() ); + if ( currentIndex < componentIndex ) + { + control->NotifyViewEvent( EMsgViewEventSetCursorLastPos, 0 ); + } + else if ( currentIndex > componentIndex ) + { + control->NotifyViewEvent( EMsgViewEventSetCursorFirstPos, 0 ); + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::EnsureCorrectScrollPartL +// +// Ensures that correct scroll part is visible. Performs scroll +// part change if focus position is outside of currently visible scroll +// part. +// --------------------------------------------------------- +// +void CMsgEditorView::EnsureCorrectScrollPartL( TInt aFocusPosition ) + { + TInt currentPart( CurrentScrollPart( aFocusPosition ) ); + if ( currentPart != iVisiblePart ) + { + MMsgEditorObserver::TMsgFocusEvent focusEvent = MMsgEditorObserver::EMsgFocusNone; + + if ( currentPart > iVisiblePart ) + { + focusEvent = MMsgEditorObserver::EMsgFocusAtBottom; + } + else + { + focusEvent = MMsgEditorObserver::EMsgFocusAtTop; + } + + MMsgEditorObserver::TMsgAfterFocusEventFunc after = + MMsgEditorObserver::EMsgAfterFocusNone; + + iVisiblePart = currentPart; + iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgHandleFocusChange, + &focusEvent, + &after, + &iVisiblePart ); + + if ( after != MMsgEditorObserver::EMsgAfterFocusNone ) + { + SetAfterFocusL( after ); + } + + EnsureCorrectFormPosition( EFalse, EFalse ); + + UpdateScrollBarL(); + } + } + +// --------------------------------------------------------- +// CMsgEditorView::CurrentScrollPart +// +// Calculates the correct scroll part of the given focus position. +// Current scroll part is evaluated based on position of top & bottom +// of the view rectangle. If either one of the borders are on the +// different scroll part than visible part then that part is returned +// as current part. +// --------------------------------------------------------- +// +TInt CMsgEditorView::CurrentScrollPart( TInt aFocusPosition ) + { + TInt result( 0 ); + + TInt currentHeight( 0 ); + TInt topPart( -1 ); + TInt bottomPart( -1 ); + + TInt topPosition( aFocusPosition ); + TInt bottomPosition( topPosition + iViewRect.Height() - iBaseLineOffset ); + + TInt screenHeight = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset; + + for ( TInt current = 0; current < iScrollParts; current++ ) + { + if ( current == iVisiblePart ) + { + currentHeight += iVisiblePartHeight; + } + else + { + currentHeight += screenHeight; + } + + if ( topPart == -1 && + topPosition < currentHeight ) + { + topPart = current; + } + + if ( bottomPart == -1 && + bottomPosition <= currentHeight ) + { + bottomPart = current; + } + } + + if ( topPart == -1 ) + { + topPart = iScrollParts - 1; + } + + if ( bottomPart == -1 ) + { + bottomPart = iScrollParts - 1; + } + + if ( topPart == bottomPart ) + { + result = topPart; + } + else + { + if ( topPart != iVisiblePart ) + { + result = topPart; + } + else + { + result = bottomPart; + } + } + return result; + } + +// --------------------------------------------------------- +// CMsgEditorView::IsFocusable +// +// Determines if given control should receive focus when navigating +// into given direction. +// +// On editor mode (i.e. non read-only) every control is focusable. +// +// On viewer mode (i.e. read-only) control is focusable if: +// - force focus stop flag is set to control, +// - control is not fully visible on the screen, +// - focus change to given direction is not allowed or +// - there is currently focused (i.e. currently shown and +// highlighted) automatic finder item. +// --------------------------------------------------------- +// +TBool CMsgEditorView::IsFocusable( CMsgBaseControl* aControl, + TMsgFocusDirection aDirection ) const + { + if ( !aControl ) + { + return EFalse; + } + + if ( aControl->IsReadOnly() ) + { + if ( aControl->ControlModeFlags() & EMsgControlModeForceFocusStop || + !ControlFullyVisible( aControl ) || + !( aControl->IsFocusChangePossible( aDirection ) ) ) + { + return ETrue; + } + else if ( aControl->ItemFinder() ) + { + const CItemFinder::CFindItemExt& item = aControl->ItemFinder()->CurrentItemExt(); + + if ( item.iItemType != CItemFinder::ENoneSelected ) + { + return ETrue; + } + } + + return EFalse; + } + + return ETrue; + } + +// --------------------------------------------------------- +// CMsgEditorView::ShowScrollPopupInfoTextL +// +// Loads the info text resouce if not yet loaded. Creates correct +// text string based on this resource and given part number. +// After this sets the created info text for the scroll bar. +// --------------------------------------------------------- +// +void CMsgEditorView::ShowScrollPopupInfoTextL( CAknDoubleSpanScrollBar* aScrollBar, TInt aPartNumber ) + { + if ( !iScrollPopText ) + { + if ( !iCoeEnv->IsResourceAvailableL( R_QTN_MSG_PAGE_NUMBER_POPUP ) ) + { + // Load the resource file containing page number popup if it has not yet + // been loaded. This should not happen but better to be prepared for that also. + TParse parse; + User::LeaveIfError( parse.Set( KMsgEditorAppUiResourceFileName, + &KDC_RESOURCE_FILES_DIR, + NULL ) ); + + TFileName* fileName = new( ELeave ) TFileName( parse.FullName() ); + CleanupStack::PushL( fileName ); + + iResourceLoader.OpenL( *fileName ); + + CleanupStack::PopAndDestroy( fileName ); + } + + iScrollPopText = StringLoader::LoadL( R_QTN_MSG_PAGE_NUMBER_POPUP, iCoeEnv ); + } + + TBuf buffer; + StringLoader::Format( buffer, *iScrollPopText, KErrNotFound, aPartNumber ); + + aScrollBar->SetScrollPopupInfoTextL( buffer ); + } + +// --------------------------------------------------------- +// CMsgEditorView::EnsureCorrectViewPosition +// --------------------------------------------------------- +// +void CMsgEditorView::EnsureCorrectViewPosition() + { + CMsgBaseControl* firstHeaderControl = iHeader->CountMsgControls() > 0 ? iHeader->MsgControl( 0 ) : NULL; + + CMsgBaseControl* lastControlBody = iBody->CountMsgControls() > 0 ? + iBody->MsgControl( iBody->CountMsgControls() - 1 ) : NULL; + + if ( firstHeaderControl && + firstHeaderControl->Position().iY > iBaseLineOffset ) + { + TInt pixelsToScroll( firstHeaderControl->Position().iY - iBaseLineOffset ); + TRAP_IGNORE( DoScrollViewL( pixelsToScroll, + EMsgScrollDown ) ); + } + else if ( lastControlBody && + lastControlBody->Rect().iBr.iY <= iViewRect.Height() - iLineHeight ) + { + TInt pixelsToScroll( iViewRect.Height() - lastControlBody->Rect().iBr.iY ); + TRAP_IGNORE( DoScrollViewL( pixelsToScroll, + EMsgScrollUp ) ); + } + else + { + + CMsgBaseControl* ctrl = FocusedControl(); + if (( ctrl && ctrl->ControlModeFlags() & EMsgControlModeBodyMaxHeight) && ctrl->VirtualHeight() < MsgEditorCommons::MaxBodyHeight() ) + { + // Special handling is required when we rotate the phone from + // landscape to portrait or vice versa when the Editor is placed last + TInt bottomYPos( ctrl->Position().iY + ctrl->VirtualHeight() ); + TInt distanceFromBottom( Rect().iBr.iY - bottomYPos ); + + if ( distanceFromBottom > 0 ) + { + TInt delta = Abs( iFormOffset ) < distanceFromBottom ? -iFormOffset : + distanceFromBottom; + if ( delta ) + { + ScrollForm( delta, ETrue ); + } + } + if ( ViewInitialized() ) + { + TRAP_IGNORE(UpdateScrollBarL()); + } + + } + else if( ctrl ) + { + TInt height( 0 ); + TInt pos( 0 ); + + GetVirtualFormHeightAndPos( height, pos ); + if(!ControlFullyVisible( ctrl ) && height > iViewRect.Height() ) + { + TInt controlPixels( Abs(ctrl->Position().iY )); + MsgEditorCommons::RoundToPreviousLine( controlPixels, iLineHeight ); + TRAP_IGNORE(DoScrollViewL(controlPixels,EMsgScrollDown)); + } + } + } + } + +// --------------------------------------------------------- +// CMsgEditorView::DoScrollViewL +// +// First & last loaded control is +// needed in order to do bound checking so that empty space above +// or below loaded controls isn't shown. Actual scrolling is performed +// so that control from first or last visible (depends about the +// scrolling direction) line is retrieved. If this control isn't fully +// visible on the scroll we try to move it as much visible as allowed +// by the scrollable pixels. When the control is fully visible it can +// perform internal scrolling if needed. When it has finished internal +// scrolling then we move the view one line up or down. This is continued +// as long as there is pixels left. After all scrolling is done we +// update our internal view focus position +// that keeps count where the focus position on the view is really +// located. Finally we draw the screen if needed. +// --------------------------------------------------------- +// +void CMsgEditorView::DoScrollViewL( TInt& aPixelsToScroll, + TMsgScrollDirection aDirection ) + { + MEBLOGGER_ENTERFN( "CMsgEditorView::DoScrollViewL" ); + + TInt pixelsLeftToScroll( aPixelsToScroll ); + + TInt directionMultiplier( -1 ); + if ( aDirection == EMsgScrollDown ) + { + directionMultiplier = 1; + } + + CMsgBaseControl* firstControl = iHeader->CountMsgControls() > 0 ? iHeader->MsgControl( 0 ) : + iBody->MsgControl( 0 ); + + CMsgBaseControl* lastControl = iBody->CountMsgControls() > 0 ? + iBody->MsgControl( iBody->CountMsgControls() - 1 ) : + iHeader->MsgControl( iHeader->CountMsgControls() - 1 ); + + while ( pixelsLeftToScroll > 0 ) + { + MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::DoScrollViewL: pixelsLeftToScroll %d "), pixelsLeftToScroll ); + + CMsgBaseControl* scrolledControl = NULL; + + if ( aDirection == EMsgScrollUp ) + { + // Control located at first visible line + scrolledControl = ControlFromPosition( TPoint( iViewRect.Width() / 2, iLineHeight / 2 + iBaseLineOffset ), EFalse ); + } + else + { + // Control located at last visible line + scrolledControl = ControlFromPosition( TPoint( iViewRect.Width() / 2, iViewRect.Height() - iLineHeight / 2 ), EFalse ); + } + + if ( scrolledControl ) + { + // Assumption: Control will make internal scrolling only if + // it's height is greater or equal to view height. + if ( !ControlFullyVisible( scrolledControl ) ) + { + // Calculate how many pixels needs to be scrolled in order + // to control be fully visible. + TInt controlPixels( Abs( scrolledControl->Position().iY ) ); + + if ( aDirection == EMsgScrollDown ) + { + // Round to the previous full line. + MsgEditorCommons::RoundToPreviousLine( controlPixels, iLineHeight ); + } + else + { + // Round to the next full line. + MsgEditorCommons::RoundToNextLine( controlPixels, iLineHeight ); + } + + // Scroll form so that control is fully visible or + // at minimum one line height and at maximum pixels left to scroll. + TInt scrollFormByPixels = Max( iLineHeight, Min( pixelsLeftToScroll, + controlPixels ) ); + + TInt oldYPos( scrolledControl->Position().iY ); + + // To disable drawing in the middle of scrolling. + iStateFlags |= EMsgStateRefreshing; + + if ( CurrentScrollPart( iViewFocusPosition - aPixelsToScroll + pixelsLeftToScroll + directionMultiplier * scrollFormByPixels ) != iVisiblePart ) + { + // Do not scroll form if scrolling would make scroll part change. + break; + } + + ScrollForm( -directionMultiplier * scrollFormByPixels, EFalse ); + + // Substract the pixels really moved. + pixelsLeftToScroll -= Abs( oldYPos - scrolledControl->Position().iY ); + + MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::DoScrollViewL: pixelsLeftToScroll after fully visible %d "), pixelsLeftToScroll ); + } + + if ( pixelsLeftToScroll > 0 ) + { + // Perform internal scrolling for the control if there is still pixels left to scroll. + pixelsLeftToScroll -= scrolledControl->ScrollL( pixelsLeftToScroll, aDirection ); + + MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::DoScrollViewL: pixelsLeftToScroll after internal scrolling %d "), pixelsLeftToScroll ); + } + } + + // After component's internal scrolling there is still some pixels left to scroll + // move the view to correction direction one line. + if ( pixelsLeftToScroll > 0 ) + { + // Boundary check for form scrolling. Do not scroll up (down) if first (last) control is + // fully visible. Also do not scroll form if scrolling would make scroll part change. + if ( aDirection == EMsgScrollDown ? lastControl->Rect().iBr.iY > iViewRect.Height() : + firstControl->Rect().iTl.iY < iViewRect.Height() && + CurrentScrollPart( iViewFocusPosition - aPixelsToScroll + pixelsLeftToScroll + directionMultiplier * iLineHeight ) == iVisiblePart ) + { + + if ( pixelsLeftToScroll > 0 ) + { + // To disable drawing in the middle of scrolling. + iStateFlags |= EMsgStateRefreshing; + + ScrollForm( -directionMultiplier * iLineHeight, EFalse ); + pixelsLeftToScroll -= iLineHeight; + + MEBLOGGER_WRITEF(_L("MEB: CMsgEditorView::DoScrollViewL: pixelsLeftToScroll after single scrolling %d "), pixelsLeftToScroll ); + + } + } + else + { + break; + } + } + } + + iViewFocusPosition += directionMultiplier * ( aPixelsToScroll - pixelsLeftToScroll ); + + if ( iStateFlags & EMsgStateRefreshing ) + { + iStateFlags &= ~EMsgStateRefreshing; + DrawNow(); + } + + MEBLOGGER_LEAVEFN( "CMsgEditorView::ScrollViewL" ); + } + + +// End of File