/*
* 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 <eikenv.h> // for CEikonEnv
#include <eikappui.h> // for CEikAppUi
#include <barsread.h> // for TResourceReader
#include <eikrted.h> // for CEikRichTextEditor
#include <eikscrlb.h>
#include <AknUtils.h> // for AknUtils
#include <txtrich.h> // for automatic highlight
#include <aknappui.h>
#include <applayout.cdl.h> // LAF
#include <aknlayoutscalable_apps.cdl.h>
#include <bautils.h> // NearestLanguageFile
#include <data_caging_path_literals.hrh>
#include <aknnavi.h>
#include <aknnavide.h>
#include <AknStatuspaneUtils.h>
#include <AknsBasicBackgroundControlContext.h> // for skins support
#include <AknsDrawUtils.h>
#include <MsgEditorAppUi.rsg>
#include <StringLoader.h>
#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;
const TInt KSelectionOffset = 50;
_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 ),
iPrevFocus( EMsgNoneFocused ),
iMoveUpDownEvent( EFalse)
{
}
// ---------------------------------------------------------
// 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<CAknNavigationControlContainer*>( 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 )
{
iPrevFocus = EMsgHeaderFocused;
iCurrentFocus = EMsgBodyFocused;
newFocus = iBody->FirstFocusableControl( 0, EMsgFocusDown );
if ( newFocus == KErrNotFound )
{
iPrevFocus = EMsgNoneFocused;
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 );
}
}
iMoveUpDownEvent = ETrue;
EnsureCorrectFormPosition( ( aKeyEvent.iCode == EKeyDownArrow ) && focusRotated, forceScrollUp );
iMoveUpDownEvent = EFalse;
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( IsReadOnly() )
{
handled = HandleScrollEventL(aPointerEvent);
}
if ( !handled && 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::HandleScrollEventL
//
// Handle text scrolling from pointer event
// ---------------------------------------------------------
//
TBool CMsgEditorView::HandleScrollEventL(const TPointerEvent& aPointerEvent)
{
TBool handled = EFalse;
if ( IsFocused() )
{
switch(aPointerEvent.iType)
{
case TPointerEvent::EButton1Down:
{
if(!iScrollBar->VerticalScrollBar()->Rect().Contains(aPointerEvent.iPosition))
{
iIsScrolling = ETrue;
iHaveScrolled = EFalse;
iFirstPointerDown = aPointerEvent;
iScrollPos = aPointerEvent.iPosition;
handled = ETrue;
}
else
{
iIsScrolling = EFalse;
}
}
break;
case TPointerEvent::EDrag:
{
if(iIsScrolling)
{
if(!iHaveScrolled)
{
TInt xOffset = aPointerEvent.iPosition.iX - iScrollPos.iX;
if(Abs(xOffset) > KSelectionOffset) // this is a selection
{
iIsScrolling = EFalse;
ThrowOutPointerEventL(iFirstPointerDown);
break;
}
}
TInt yOffset = aPointerEvent.iPosition.iY - iScrollPos.iY;
if(Abs(yOffset) > iLineHeight)
{
TInt scrolled = ScrollL(yOffset);
if(scrolled)
{
iScrollPos.iY += scrolled;
}
else
{
ScrollPageL(yOffset);
iScrollPos.iY = aPointerEvent.iPosition.iY;
}
iHaveScrolled = ETrue;
}
handled = ETrue;
}
}
break;
case TPointerEvent::EButton1Up:
{
if(iIsScrolling)
{
iIsScrolling = EFalse;
if(!iHaveScrolled) // mainly perform a click
{
ThrowOutPointerEventL(iFirstPointerDown);
iFirstPointerDown.iType = TPointerEvent::EDrag;
ThrowOutPointerEventL(iFirstPointerDown);
ThrowOutPointerEventL(aPointerEvent);
}
handled = ETrue;
}
}
break;
default:
break;
}
}
return handled;
}
// ---------------------------------------------------------
// CMsgEditorView::ThrowOutPointerEventL
//
//
// Throw the pointer event to the other handler
// ---------------------------------------------------------
//
void CMsgEditorView::ThrowOutPointerEventL(const TPointerEvent& aPointerEvent)
{
TBool lastPosHandled = EFalse;
TPointerEvent pointerEvent( aPointerEvent );
iEditorObserver.EditorObserver( MMsgEditorObserver::EMsgControlPointerEvent,
ControlFromPosition( aPointerEvent.iPosition, ETrue ),
&pointerEvent, &lastPosHandled );
if ( !lastPosHandled )
{
CCoeControl::HandlePointerEventL( aPointerEvent );
}
}
// ---------------------------------------------------------
// CMsgEditorView::ScrollPartL
//
// Move current view up or down with screen height.
//
// ---------------------------------------------------------
//
void CMsgEditorView::ScrollPartL(TInt aOffset)
{
TKeyEvent keyEvent;
keyEvent.iModifiers = 0;
if(aOffset < 0)
{
keyEvent.iCode = EKeyDownArrow;
keyEvent.iScanCode = EStdKeyDownArrow;
}
else
{
keyEvent.iCode = EKeyUpArrow;
keyEvent.iScanCode = EStdKeyUpArrow;
}
OfferKeyEventL(keyEvent, EEventKeyDown);
OfferKeyEventL(keyEvent, EEventKey);
OfferKeyEventL(keyEvent, EEventKeyUp);
CItemFinder* finder = ItemFinder();
if(finder)
{
finder->ResetCurrentItem();
}
}
// ---------------------------------------------------------
// CMsgEditorView::ScrollPageL
//
// Change to the previous or the next part(slide) if possible.
//
// ---------------------------------------------------------
//
void CMsgEditorView::ScrollPageL(TInt aOffset)
{
CMsgBaseControl* headerFocus = iHeader ? iHeader->FocusedControl() : NULL;
CMsgBaseControl* bodyFocus = iBody ? iBody->FocusedControl() : NULL;
if(aOffset < 0)
{
if ( iCurrentFocus == EMsgHeaderFocused )
{
iCurrentFocus = EMsgBodyFocused;
}
if(headerFocus)
{
headerFocus->NotifyViewEvent( EMsgViewEventPrepareFocusTransitionDown, 0 );
}
if(bodyFocus)
{
TBool isFocused = bodyFocus->IsFocused();
bodyFocus->SetFocus(ETrue);
bodyFocus->NotifyViewEvent( EMsgViewEventPrepareFocusTransitionDown, 0 );
bodyFocus->SetFocus(isFocused);
}
}
else
{
if(bodyFocus)
{
TBool isFocused = bodyFocus->IsFocused();
bodyFocus->SetFocus(ETrue);
bodyFocus->NotifyViewEvent( EMsgViewEventPrepareFocusTransitionUp, 0 );
bodyFocus->SetFocus(isFocused);
}
if(headerFocus)
{
headerFocus->NotifyViewEvent( EMsgViewEventPrepareFocusTransitionUp, 0 );
}
if ( iCurrentFocus==EMsgBodyFocused && 0==iVisiblePart)
{
iCurrentFocus = EMsgHeaderFocused;
}
}
ScrollPartL(aOffset);
}
// ---------------------------------------------------------
// 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<CMsgBaseControl*>( aControl );
if ( baseControl &&
( baseControl->ControlType() == EMsgBodyControl ||
baseControl->ControlType() == EMsgXhtmlBodyControl ) &&
baseControl->ItemFinder() &&
static_cast<CMsgBodyControl*>( 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<CMsgBaseControl*>( 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;
iPrevFocus = 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<CMsgBodyControl*>( 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<CMsgExpandableControl*>( aControl )->Editor();
}
else if ( aControl->ControlType() == EMsgBodyControl ||
aControl->ControlType() == EMsgXhtmlBodyControl )
{
editor = &static_cast<CMsgBodyControl*>( 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<CMsgBodyControl*>( 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<CMsgBodyControl*>( focusedControl )->Editor();
editor.SetCursorPosL( editor.TextLength(), EFalse );
}
else if ( focusedControl->ControlType() == EMsgExpandableControl ||
focusedControl->ControlType() == EMsgAddressControl )
{
CEikRichTextEditor& editor =
static_cast<CMsgExpandableControl*>( focusedControl )->Editor();
editor.SetCursorPosL( editor.TextLength(), EFalse );
if ( focusedControl->ControlType() == EMsgAddressControl )
{
// Update the address field highlighting
static_cast<CMsgAddressControlEditor&>( editor ).CheckHighlightingL();
}
}
}
else if ( aDirection == EMsgFocusUp &&
iVisiblePart == 0)
{
if ( focusedControl->ControlType() == EMsgBodyControl ||
focusedControl->ControlType() == EMsgXhtmlBodyControl )
{
CEikRichTextEditor& editor = static_cast<CMsgBodyControl*>( focusedControl )->Editor();
editor.SetCursorPosL( 0, EFalse );
}
else if ( focusedControl->ControlType() == EMsgExpandableControl ||
focusedControl->ControlType() == EMsgAddressControl )
{
CEikRichTextEditor& editor =
static_cast<CMsgExpandableControl*>( focusedControl )->Editor();
editor.SetCursorPosL( 0, EFalse );
if ( focusedControl->ControlType() == EMsgAddressControl )
{
// Update the address field highlighting
static_cast<CMsgAddressControlEditor&>( editor ).CheckHighlightingL();
}
}
}
}
}
if ( startAutoFind )
{
CMsgBaseControl* ctrl = (CMsgBaseControl*)iBody->MsgControl( newFocus );
if ( ctrl &&
( ctrl->ControlType() == EMsgBodyControl ||
ctrl->ControlType() == EMsgXhtmlBodyControl ) &&
ctrl->ItemFinder() &&
static_cast<CMsgBodyControl*>( 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;
}
else
{
if ( ctrl->ControlType() == EMsgBodyControl
&& ctrl->ItemFinder()
&& ctrl->IsReadOnly()
&& !iMoveUpDownEvent )
{
TInt countHeader = iHeader->CountMsgControls();
for ( TInt i = 0; i < countHeader; i++ )
{
CMsgBaseControl* headerCtrl = iHeader->MsgControl( i );
if ( headerCtrl )
{
TInt controlType;
controlType = headerCtrl->ControlType();
if ( controlType == EMsgAddressControl ||
controlType == EMsgExpandableControl ||
controlType == EMsgAttachmentControl )
{
if ( ControlFullyVisible(headerCtrl) || iPrevFocus == EMsgHeaderFocused )
{
return EFalse; // Ignoring Scroll Event
}
}
}
}
}
}
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<CMsgBodyControl*>( ControlById( EMsgComponentIdBody ) );
CEikRichTextEditor* bodyEditor = NULL;
TBool toBodyBeginning = (aAfterFocus == MMsgEditorObserver::EMsgCursorToBodyBeginning);
if ( body )
{
bodyEditor = &body->Editor();
body->SetupAutomaticFindAfterFocusChangeL( toBodyBeginning );
}
if ( toBodyBeginning )
{
newFocus = iBody->FirstFocusableControl( 0, EMsgFocusDown );
if ( newFocus != KErrNotFound )
{
ctrl = iBody->MsgControl( newFocus );
SetFocusByControlIdL( ctrl->ControlId() );
if ( bodyEditor )
{
bodyEditor->SetCursorPosL( 0, EFalse );
}
}
// go to the first line
CMsgBaseControl* bodyFocus = iBody->FocusedControl();
if(bodyFocus) bodyFocus->NotifyViewEvent( EMsgViewEventPrepareFocusTransitionUp, 0 );
EnsureCorrectFormPosition(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;
}
// go to the last line
CMsgBaseControl* bodyFocus = iBody->FocusedControl();
if(bodyFocus) bodyFocus->NotifyViewEvent( EMsgViewEventPrepareFocusTransitionDown, 0 );
EnsureCorrectFormPosition(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<CMsgExpandableControl*>( control )->Editor();
}
else if ( control->ControlType() == EMsgBodyControl ||
control->ControlType() == EMsgXhtmlBodyControl )
{
editor = &static_cast<CMsgBodyControl*>( 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();
if( IsReadOnly() )
{
TInt screenHeight = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset;
if(scrolledPixels > screenHeight)
{
scrolledPixels = screenHeight;
}
else if(scrolledPixels < -screenHeight)
{
scrolledPixels = -screenHeight;
}
}
// 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<CAknDoubleSpanScrollBar*>( 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 = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset;
TInt singlePartHeight = Max( screenHeight, iVisiblePartHeight / 10 );
for ( TInt current = 0; current < currentPart; current++ )
{
if ( current == iVisiblePart )
{
currentHeight += iVisiblePartHeight;
}
else
{
currentHeight += singlePartHeight;
}
}
iScrollBar->DrawBackground( ETrue, EFalse );
iScrollBar->SetVFocusPosToThumbPos( currentHeight + screenHeight/20 );
if ( currentPart != iPopUpPart )
{
ShowScrollPopupInfoTextL( static_cast<CAknDoubleSpanScrollBar*>( 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::ScrollL
//
// Scroll text in current part (slide)
// ---------------------------------------------------------
//
TInt CMsgEditorView::ScrollL(TInt aScrolledPixels)
{
TInt scrolledPixels = aScrolledPixels;
TInt focusPos = iViewFocusPosition;
TInt currentPart( CurrentScrollPart( AknScrollBarModel()->FocusPosition() ) );
if ( currentPart == iVisiblePart )
{
if ( iPopUpPart != -1 )
{
// Hide the popup if visible
static_cast<CAknDoubleSpanScrollBar*>( iScrollBar->VerticalScrollBar() )->SetScrollPopupInfoTextL( KNullDesC );
iPopUpPart = -1;
}
}
if ( iPopUpPart == -1 )
{
// Round to the previous full line.
MsgEditorCommons::RoundToPreviousLine( scrolledPixels, iLineHeight );
if ( scrolledPixels != 0 )
{
ScrollViewL( Abs( scrolledPixels ), scrolledPixels>0?EMsgScrollUp:EMsgScrollDown, EFalse );
EnsureCorrectScrollPartL( AknScrollBarModel()->FocusPosition() );
}
}
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;
TInt screenHeight = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset;
TInt singlePartHeight = Max( screenHeight, iVisiblePartHeight / 10 );
for ( TInt current = 0; current < currentPart; current++ )
{
if ( current == iVisiblePart )
{
currentHeight += iVisiblePartHeight;
}
else
{
currentHeight += singlePartHeight;
}
}
iScrollBar->DrawBackground( ETrue, EFalse );
iScrollBar->SetVFocusPosToThumbPos( currentHeight );
if ( currentPart != iPopUpPart )
{
ShowScrollPopupInfoTextL( static_cast<CAknDoubleSpanScrollBar*>( iScrollBar->VerticalScrollBar() ),
currentPart + 1 );
iPopUpPart = currentPart;
}
}
else
{
iScrollBar->SetVFocusPosToThumbPos( iViewFocusPosition );
iScrollBar->DrawScrollBarsNow();
}
return (focusPos - iViewFocusPosition);
}
// ---------------------------------------------------------
// 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;
TInt singlePartHeight = Max( screenHeight, iVisiblePartHeight / 10 );
for ( TInt current = 0; current < iVisiblePart ; current++ )
scrollTillPosition += singlePartHeight;
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 screenHeight = MsgEditorCommons::EditorViewHeigth() - iBaseLineOffset;
TInt singlePartHeight = Max( screenHeight, iVisiblePartHeight / 10 );
for ( TInt current = 0; current < iScrollParts; current++ )
{
if ( current == iVisiblePart )
{
currentHeight += iVisiblePartHeight;
bottomPart = current;
}
else
{
currentHeight += singlePartHeight;
}
if ( topPart == -1 &&
topPosition < currentHeight )
{
topPart = 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<KMsgMaximumScrollPartLength> 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.
TBool needScroll = EFalse;
TInt height = iViewRect.Height();
if(aDirection == EMsgScrollDown)
{
TInt length = lastControl->Rect().iBr.iY;
needScroll = (length > height);
}
else
{
TInt length = firstControl->Rect().iTl.iY;
needScroll = (length < height);
}
TBool isCurrent = (CurrentScrollPart( iViewFocusPosition - aPixelsToScroll + pixelsLeftToScroll + directionMultiplier * iLineHeight ) == iVisiblePart);
if ( needScroll && isCurrent )
{
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