/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  MsgAttachmentControl implementation
*
*/



// ========== INCLUDE FILES ================================

#include <barsread.h>                      // for TResourceReader
#include <txtrich.h>                       // for CRichText

#include <eikenv.h>                        // for CEikonEnv
#include <eikappui.h>                      // for CEikAppUi
#include <eikedwin.h>                      // for TClipboardFunc
#include <eiklabel.h>

#include <AknUtils.h>                      // for AknUtils
#include <aknbutton.h>                     // for CAknButton

#include <MsgAttachmentModel.h>
#include <MsgAttachmentInfo.h>

#include <gulicon.h>
#include <AknsUtils.h>
#include <AknsConstants.h>
#include <aknlayoutscalable_apps.cdl.h>

#include <data_caging_path_literals.hrh>
#include <msgeditor.mbg>

#ifdef RD_TACTILE_FEEDBACK
#include <touchfeedback.h>
#endif 

#include "MsgAttachmentControl.h"             // for CMsgAttachmentControl
#include "MsgExpandableControlEditor.h"       // for CMsgExpandableControlEditor
#include "MsgBaseControlObserver.h"        // for MMsgBaseControlObserver
#include "MsgEditorView.h"
#include "MsgEditorPanic.h"                // for CMsgEditor panics
#include "MsgEditorCommon.h"               // for KArabicSemicolon
#include "MsgEditorCustomDraw.h"           // for CMsgEditorCustomDraw

// ========== EXTERNAL DATA STRUCTURES =====================

// ========== EXTERNAL FUNCTION PROTOTYPES =================

// ========== CONSTANTS ====================================

// Default color for EAknsCIQsnIconColorsCG13 group icons
const TInt KDefaultAttachmentColor = 215; 

// ========== MACROS =======================================

// ========== LOCAL CONSTANTS AND MACROS ===================

const TInt KAttachmentArrayGranularity = 10;

_LIT( KAttachmentListSeparator, ", " );
_LIT( KIconMbmFile, "msgeditor.mbm" );
_LIT( KStripList, "\x000A\x000D\x2029\x2028\x0085" );

const TText KZeroWidthJoiner = 0x200D;
const TText KLRMarker = 0x200E;
const TText KRLMarker = 0x200F;

const TInt KScrollStartDelay = 1500000; // 1,5 s
const TInt KScrollEndDelay = 500000;    // 0,5 s
const TInt KScrollRestartDelay = 3000000; // 3 s.
const TInt KScrollTickDelay = 135000; // 0,135s
const TInt KScrollJumpAreaDivider = 15;

// ========== MODULE DATA STRUCTURES =======================

// ========== LOCAL FUNCTION PROTOTYPES ====================

// ========== LOCAL FUNCTIONS ==============================

// ========== MEMBER FUNCTIONS =============================


// ---------------------------------------------------------
// CMsgAttachmentControl::CMsgAttachmentControl
//
// Constructor.
// ---------------------------------------------------------
//
CMsgAttachmentControl::CMsgAttachmentControl( 
                                    MMsgBaseControlObserver& aBaseControlObserver,
                                    CMsgEditorView& aView ) : 
    CMsgExpandableControl( aBaseControlObserver ),
    iScrollPos( EScrollPosBeginning )
    {
    iControlType = EMsgAttachmentControl;
    iControlId = EMsgComponentIdAttachment;
    
    SetMopParent( &aView );
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ConstructL
//
// 2nd phase constructor. After base class initialization creates
// attachment array that holds the attachment name strings. Empty
// caption control is also created for base class purposes. NOTE! This is 
// something that would be good to get rid off. Initializes editor control
// and sets itself as it's edwin event, edwin size event and
// control event observer. Creates editor is set as readonly and cursor is
// hidden. Focus must stop to this control when navigating. No formatting
// is allowed. Label icon and if pen input (i.e. touch screen) is enabled
// then also button are created. Initializes marquee scroll timer and
// layout related parameters.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::ConstructL()
    {
    BaseConstructL();  // Sets margins only.
    
    iAttachmentArray = new( ELeave ) CDesCArrayFlat( KAttachmentArrayGranularity );

    // Create empty caption. (Needed by base class.)
    iCaption = new( ELeave ) CEikLabel;

    // Create editor.
    iEditor = new( ELeave ) CMsgExpandableControlEditor( this, 
                                                         iControlModeFlags, 
                                                         iBaseControlObserver );
    iEditor->SetControlType(iControlType);   
    iEditor->ConstructL();
    iEditor->SetObserver( this );
    iEditor->SetEdwinSizeObserver( this );
    iEditor->AddEdwinObserverL( this );
    iEditor->SetReadOnly( ETrue );
    iEditor->AddFlagToUserFlags( CEikEdwin::EAvkonDisableCursor );
    
    iControlModeFlags |= EMsgControlModeForceFocusStop;
    
    SetPlainTextMode( ETrue );
    
    iLabelIcon = CreateAttachmentIconsL();
    
#ifdef RD_SCALABLE_UI_V2
    if ( AknLayoutUtils::PenEnabled() )
        {
        CreateButtonL();
        }
#endif // RD_SCALABLE_UI_V2
    
    iScrollTimer = CPeriodic::NewL( EPriorityNormal );
    
    ResolveLayoutsL();
    }

// ----------------------------------------------------------------------------
// CMsgAttachmentControl::NewL
// Symbian two phased constructor
// ----------------------------------------------------------------------------
//
EXPORT_C CMsgAttachmentControl* CMsgAttachmentControl::NewL( 
                                                MMsgBaseControlObserver& aBaseControlObserver,
                                                CMsgEditorView& aView )
    {
    CMsgAttachmentControl* self = new( ELeave ) CMsgAttachmentControl( aBaseControlObserver,
                                                                       aView );

    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );

    return self;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::~CMsgAttachmentControl
//
// Destructor.
// ---------------------------------------------------------
//
CMsgAttachmentControl::~CMsgAttachmentControl()
    {
    if ( iAttachmentArray )
        {
        iAttachmentArray->Reset();
        }
        
    delete iAttachmentArray;
    delete iLabelIcon;
    delete iScrollTimer;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::AppendAttachmentL
//
//
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::AppendAttachmentL( const TDesC& aName )
    {
    InsertAttachmentL( aName, Count() );
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::InsertAttachmentL
//
//
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::InsertAttachmentL( const TDesC& aName, TInt aIndex )
    {
    AddToAttachmentArrayL( aName, aIndex );
    
    UpdateControlL();
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::AddAttachmentL
//
//
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::AddAttachmentsL( const MDesCArray& aNames )
    {
    Reset();
    
    for ( TInt i = 0; i < aNames.MdcaCount(); i++ )
        {
        AddToAttachmentArrayL( aNames.MdcaPoint( i ), Count() );
        }
        
    UpdateControlL();
    }


// ---------------------------------------------------------
// CMsgAttachmentControl::AddAttachmentL
//
//
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::AddAttachmentsL( const CMsgAttachmentModel& aModel )
    {
    Reset();
    
    TParse* parser = new( ELeave ) TParse ;
    CleanupStack::PushL( parser );
    
    for ( TInt i = 0; i < aModel.NumberOfItems(); i++ )
        {
        parser->Set( aModel.AttachmentInfoAt( i ).FileName(),NULL, NULL );
        AddToAttachmentArrayL( parser->NameAndExt(), Count() );
        }
    
    CleanupStack::PopAndDestroy( parser ); 
    UpdateControlL();
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::RemoveAttachmentL
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::RemoveAttachmentL( const TDesC& aName )
    {
    HBufC* convertedFileName = AknTextUtils::ConvertFileNameL( aName );
    CleanupStack::PushL( convertedFileName );
    
    TPtr convertedFileNamePtr = convertedFileName->Des();
    
    AknTextUtils::StripCharacters( convertedFileNamePtr, KAknStripListControlChars );
    
    TInt pos( KErrNotFound );
    if ( iAttachmentArray->Find( *convertedFileName, pos ) == 0 )
        {
        RemoveAttachmentL( pos );
        }
    
    CleanupStack::PopAndDestroy( convertedFileName );
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::RemoveAttachmentL
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::RemoveAttachmentL( TInt aIndex )
    {
    iAttachmentArray->Delete( aIndex );
    UpdateControlL();
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::Reset
//
// from CMsgBaseControl.
// Same as above but non-leaving version.
// ---------------------------------------------------------
//
EXPORT_C void CMsgAttachmentControl::Reset()
    {
    if ( iAttachmentArray )
        {
        iAttachmentArray->Reset();
        }
    if ( iEditor )
        {
        iEditor->Reset();
        }
    }


// ---------------------------------------------------------
// CMsgAttachmentControl::Count
// ---------------------------------------------------------
//
EXPORT_C TInt CMsgAttachmentControl::Count() const
    {
    return iAttachmentArray->MdcaCount();
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::Attachment
// ---------------------------------------------------------
//
EXPORT_C TPtrC CMsgAttachmentControl::Attachment( TInt aIndex ) const
    {
    return iAttachmentArray->MdcaPoint( aIndex );
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ConstructFromResourceL
//
// Creates this control from resource.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::ConstructFromResourceL( TInt aResourceId )
    {
    TResourceReader reader;
    iCoeEnv->CreateResourceReaderLC( reader, aResourceId );

    // Read some information about control from resource.
    ReadControlPropertiesFromResourceL( reader );
    
    ConstructL();
    
    CleanupStack::PopAndDestroy();  // reader
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::EditPermission
//
// Checks and returns control's edit permissions. Edit permissions are needed
// to check in order to know whether some key press is allowed to pass
// to the control or not.
// ---------------------------------------------------------
//
TUint32 CMsgAttachmentControl::EditPermission() const
    {
    return CMsgBaseControl::EMsgEditNone;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::NotifyViewEvent
//
// Builds up text fields for the control i.e. reads verified recipients and
// creates corresponding text fields for them.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::NotifyViewEvent( TMsgViewEvent aEvent, TInt aParam )
    {
    if ( IsReadOnly() )
        {
        aParam |= EMsgViewEventAutoHighlight;
        }

    if ( aEvent != EMsgViewEventSetCursorFirstPos &&
         aEvent != EMsgViewEventSetCursorLastPos )
        {
        CMsgExpandableControl::NotifyViewEvent( aEvent, aParam );
        }
    }


// ---------------------------------------------------------
// CMsgAttachmentControl::PrepareForReadOnly
// ---------------------------------------------------------
//
void CMsgAttachmentControl::PrepareForReadOnly( TBool aReadOnly )
    {
    if ( aReadOnly )
        {
        TRAP_IGNORE( iEditor->SetHighlightStyleL( EEikEdwinHighlightLink ) );
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::CountComponentControls
//
// Returns a number of controls.
// ---------------------------------------------------------
//
TInt CMsgAttachmentControl::CountComponentControls() const
    {
    TInt result( 1 );
    
    if ( iButton )
        {
        result++;
        }
        
    return result;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ComponentControl
//
// Returns a control of index aIndex.
// ---------------------------------------------------------
//
CCoeControl* CMsgAttachmentControl::ComponentControl( TInt aIndex ) const
    {
    CCoeControl* result = NULL;
    switch ( aIndex )
        {
        case 0:
            {
            result = iEditor;
            break;
            }
        case 1:
            {
            result = iButton;
            break;
            }
        default:
            {
            break;
            }
        }
        
    return result;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::OfferKeyEventL
//
// Handles key events. The most of the keys are passed to the control editor.
// ---------------------------------------------------------
//
TKeyResponse CMsgAttachmentControl::OfferKeyEventL(
    const TKeyEvent& /*aKeyEvent*/, TEventCode /*aType*/ )
    {
    return EKeyWasNotConsumed;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::HandleResourceChange
//
// Handles resouce change events. Event are passed to the base
// class and if layout has been switched new layouts are resolved
// from LAF.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::HandleResourceChange( TInt aType )
    {
    TRAP_IGNORE( 
        {
        DoHandleResourceChangeBeforeL( aType );
    
        CMsgExpandableControl::HandleResourceChange( aType );
        
        DoHandleResourceChangeAfterL( aType );
        } );    
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::HandlePointerEventL
//
// Pointer event handling is disabled on editor
// ---------------------------------------------------------
//
void CMsgAttachmentControl::HandlePointerEventL( const TPointerEvent& aPointerEvent )
    {
    if ( !iEditor->Rect().Contains( aPointerEvent.iPosition ) )
        {
        CMsgExpandableControl::HandlePointerEventL( aPointerEvent );
        }
#ifdef RD_TACTILE_FEEDBACK 
    else if ( aPointerEvent.iType == TPointerEvent::EButton1Down )
        {
        MTouchFeedback* feedback = MTouchFeedback::Instance();
        if ( feedback )
            {
            feedback->InstantFeedback( this, ETouchFeedbackBasic );                        
            }                                     
        }
#endif
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::FocusChanged
//
// This is called when the focus of the control is changed.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::FocusChanged( TDrawNow aDrawNow )
    {
    CMsgExpandableControl::FocusChanged( aDrawNow );
        
    if ( IsReadOnly() )
        {
        TRAP_IGNORE( UpdateFocusL() );
        }
    
    if ( iAutomaticScrolling )
        {
        StopAutomaticScrolling();
        
        if ( IsFocused() )
            {
            StartAutomaticScrolling( KScrollStartDelay );
            }
        else
            {
            // Reset text to the beginning when focus is lost.
            iScrollPos = EScrollPosBeginning;
            TRAP_IGNORE( SetTextForEditorL( EFalse, ETrue ) );                
            
            // Reset the text view to it's original position.
            iEditor->TextView()->SetLeftTextMargin( 0 );
            }
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::Draw
// Attachment icon is drawn to the line indicated by LAF x 
// coordinate and controls current y coordinate. 
// Attachment icon is also centered vertically to current 
// control height.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::Draw( const TRect& /*aRect*/ ) const
    {
    if ( !iButton )
        {
        TPoint tlc( iIconLayout.Rect().iTl.iX, Position().iY );
        tlc.iY += ( Size().iHeight - iIconLayout.Rect().Size().iHeight ) / 2;
    
        SystemGc().BitBltMasked( tlc,
                                 iLabelIcon->Bitmap(), 
                                 iIconLayout.Rect().Size(),
                                 iLabelIcon->Mask(),
                                 EFalse );
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ActivateL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::ActivateL()
    {
    CMsgExpandableControl::ActivateL();
    
    // Needs to be called after CEikEdwin::ActivateL as
    // panics if CEikEdwin::iTextView is not present
    iEditor->SetWordWrapL( EFalse );
    
    // Cursor is set to the beginning of text.
    iEditor->SetCursorPosL( 0, EFalse );
    
    if ( iEditor->TextView() )
        {
        const CFont* font = iEditorLayout.Font();
        TInt scrollJump( font->WidthZeroInPixels() );
        if ( scrollJump == 0 )
            {
            // Font gave us invalid scrollJump have to make
            // estimate according to scroll area.
            scrollJump = iEditorLayout.TextRect().Width() / KScrollJumpAreaDivider;
            }
        iEditor->TextView()->SetHorizontalScrollJump( scrollJump );
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::UpdateControlL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::UpdateControlL()
    {
    SetTextForEditorL( ETrue, ETrue );
    
    if ( iScrollTimer->IsActive() )
        {
        // Restart scrolling when attachment list is modified.
        iScrollPos = EScrollPosBeginning;
        StartAutomaticScrolling( KScrollStartDelay );        
        }
    
    DrawDeferred();
    iControlModeFlags |= EMsgControlModeModified;
    }


// ---------------------------------------------------------
// CMsgAttachmentControl::SetTextForEditorL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::SetTextForEditorL( TBool aContentChanged, TBool aClipText )
    {
    TInt count = iAttachmentArray->Count();
    TInt length = 0;
    
    for ( TInt i = 0; i < count; i++ )
        {
        length += iAttachmentArray->MdcaPoint( i ).Length();
        length += KAttachmentListSeparator().Length();
        }

    HBufC* buf = HBufC::NewLC( length );
    TPtr temp = buf->Des();
    
    for ( TInt j = 0; j < count; j++ )
        {
        temp.Append( iAttachmentArray->MdcaPoint( j ) );
        if ( j < count - 1 )
            {
            temp.Append( KAttachmentListSeparator );
            }
        }
        
    const CFont* font = iEditorLayout.Font();

    TInt clipWidth = 0;
    
    if ( iEditor->TextView() ) 
        {
        clipWidth = iEditor->TextView()->ViewRect().Width();
        }
    else
        {
        clipWidth = iEditorLayout.TextRect().Width();
        }
    
    TBool enableAutomaticScrolling( ETrue );
    if ( temp.Length() == 0 )
        {
        // Disable all processing if field is empty.
        enableAutomaticScrolling = EFalse;
        aContentChanged = EFalse;
        }
        
    if ( aContentChanged )
        {
        // Attachment array has been updated.
        TInt fits = font->TextCount( temp, clipWidth );

        if ( fits >= temp.Length() )
            {
            // All text fits to the field. No automatic scrolling.
            enableAutomaticScrolling = EFalse;
            }
        }
    
    if ( enableAutomaticScrolling )
        {
        EnableAutomaticScrolling();
        
        if ( aClipText )
            {
            ClipTextL( temp, clipWidth );
            }
        }
    else
        {
        DisableAutomaticScrolling();
        }
    
    // Replaces old text with new text.
    iEditor->SetTextL( buf );

    if ( IsActivated() && iEditor->CursorPos() != 0 )
        {
        // Cursor is set to the beginning of text.
        // Should not be called for non-activated control.
        iEditor->SetCursorPosL( 0, EFalse );
        }        
    
    CleanupStack::PopAndDestroy( buf );
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ResolveLayoutsL
// 
// ---------------------------------------------------------
//
void CMsgAttachmentControl::ResolveLayoutsL()
    {
    TAknLayoutRect msgTextPane;
    msgTextPane.LayoutRect( MsgEditorCommons::MsgDataPane(),
                            AknLayoutScalable_Apps::msg_text_pane( 0 ).LayoutLine() );

    TAknLayoutRect msgHeaderPane;
    msgHeaderPane.LayoutRect( msgTextPane.Rect(),
                              AknLayoutScalable_Apps::msg_header_pane().LayoutLine() );
    
    iIconLayout.LayoutRect( msgHeaderPane.Rect(),
                            AknLayoutScalable_Apps::msg_header_pane_g1().LayoutLine() );
    
    CMsgExpandableControl::ResolveLayoutsL();
        
    if ( iButton )
        {
        iButton->SetSize( iButton->Size() );
        }
    else
        {
        User::LeaveIfError( AknIconUtils::SetSize( iLabelIcon->Bitmap(), 
                                                   iIconLayout.Rect().Size() ) );
        }
     if ( iEditor->MsgEditorCustomDraw() )
        {
            iEditor->MsgEditorCustomDraw()->ResolveLayouts();
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::CreateAttachmentIconsL
// ---------------------------------------------------------
//
CGulIcon* CMsgAttachmentControl::CreateAttachmentIconsL()
    {
    TParse fileParse;
    fileParse.Set( KIconMbmFile, &KDC_APP_BITMAP_DIR, NULL );
    
    CFbsBitmap* bitmap = NULL;
    CFbsBitmap* mask = NULL;
    
    AknsUtils::CreateColorIconLC( AknsUtils::SkinInstance(),
                                  KAknsIIDQgnIndiMceAttach,
                                  KAknsIIDQsnIconColors,
                                  EAknsCIQsnIconColorsCG13,
                                  bitmap, 
                                  mask,
                                  fileParse.FullName(), 
                                  EMbmMsgeditorQgn_indi_mce_attach,
                                  EMbmMsgeditorQgn_indi_mce_attach_mask,
                                  AKN_LAF_COLOR( KDefaultAttachmentColor ) ); 
    
    CGulIcon* result = CGulIcon::NewL( bitmap, mask ); // Ownership transferred
    CleanupStack::Pop( 2 ); // bitmap, mask
    
    return result;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::CreateButtonL
// ---------------------------------------------------------
//
#ifdef RD_SCALABLE_UI_V2
void CMsgAttachmentControl::CreateButtonL()
    {
    delete iButton;
    iButton = NULL;
    
    CGulIcon* buttonIcon = CGulIcon::NewL( iLabelIcon->Bitmap(), iLabelIcon->Mask() );
    buttonIcon->SetBitmapsOwnedExternally( ETrue ); // bitmaps owned by iLabelIcon
    
    CleanupStack::PushL( buttonIcon );
    
    iButton = CAknButton::NewL( buttonIcon,
                                NULL,
                                NULL,
                                NULL,
                                KNullDesC,
                                KNullDesC,
                                KAknButtonSizeFitText,
                                0 );
        
    CleanupStack::Pop( buttonIcon ); // buttonIcon owned by iButton
    
    iButton->SetIconScaleMode( EAspectRatioPreserved );
    
    iButton->SetObserver( this );
    
    if ( DrawableWindow() )
        {
        iButton->SetContainerWindowL( Window() );
        iButton->ActivateL();
        }
    }
#else
void CMsgAttachmentControl::CreateButtonL()
    {
    }
#endif // RD_SCALABLE_UI_V2


// ---------------------------------------------------------
// CMsgAttachmentControl::DoScrollTextL
// ---------------------------------------------------------
//
TInt CMsgAttachmentControl::DoScrollTextL( TAny* aObject )
    {
    // cast, and call non-static function
    CMsgAttachmentControl* control = static_cast<CMsgAttachmentControl*>( aObject );
    TRAPD( error ,control->ScrollTextL() );
    if ( error != KErrNone )
        {
        control->StopAutomaticScrolling();
        User::Leave( error );
        }
    return KErrNone;
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ScrollTextL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::ScrollTextL()
    {
    TBool scrollText( ETrue );
    if ( iScrollPos == EScrollPosBeginning )
        {
        SetTextForEditorL( EFalse, EFalse );
        }
    else if ( iScrollPos == EScrollPosEnd )
        {
        // Indicates that text has been scrolled to the end and should be restarted.
        SetTextForEditorL( EFalse, ETrue );
        iScrollPos = EScrollPosBeginning;
        scrollText = EFalse;
        
        // Reset the text view to it's original position.
        iEditor->TextView()->SetLeftTextMargin( 0 );
        DrawNow();
        
        StartAutomaticScrolling( KScrollRestartDelay );
        }
    
    if ( scrollText )
        {
        TInt scrolledPixels = iEditor->TextView()->ScrollDisplayL( TCursorPosition::EFRight );
        if ( scrolledPixels == 0 )
            {
            // Indicates that text has been scrolled to the end. 
            iScrollPos = EScrollPosEnd;
            StartAutomaticScrolling( KScrollEndDelay ); 
            }
        else
            {
            iScrollPos = EScrollPosOther;
            }
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::DisableAutomaticScrolling
// ---------------------------------------------------------
//
void CMsgAttachmentControl::DisableAutomaticScrolling()
    {
    iAutomaticScrolling = EFalse;    
    StopAutomaticScrolling();
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::EnableAutomaticScrolling
// ---------------------------------------------------------
//
void CMsgAttachmentControl::EnableAutomaticScrolling()
    {
    iAutomaticScrolling = ETrue;
    
    if ( !iScrollTimer->IsActive() && 
         IsFocused() )
        {
        StartAutomaticScrolling( KScrollStartDelay );
        }
    }


// ---------------------------------------------------------
// CMsgAttachmentControl::StartAutomaticScrolling
// ---------------------------------------------------------
//
void CMsgAttachmentControl::StartAutomaticScrolling( TInt aStartDelay )
    {
    StopAutomaticScrolling();
    iScrollTimer->Start( aStartDelay,
                         KScrollTickDelay,
                         TCallBack( DoScrollTextL, this ) );
    }
        
// ---------------------------------------------------------
// CMsgAttachmentControl::StopAutomaticScrolling
// ---------------------------------------------------------
//
void CMsgAttachmentControl::StopAutomaticScrolling()
    {
    iScrollTimer->Cancel();
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::ClipTextL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::ClipTextL( TPtr aText, TInt aClipWidth )
    {
    const CFont* font = iEditorLayout.Font();
    
    TInt fits = font->TextCount( aText, aClipWidth );
        
    if ( fits < aText.Length() )
        {
        // Clipping is actually done only if text is at the beginning.
        TInt ellipsisWidth = font->CharWidthInPixels( KEllipsis );

        fits = font->TextCount( aText, aClipWidth - ellipsisWidth );
        
        // Work out the text directionality before the truncation point.
        // First put the text in reverse order and then call
        // TBidiText::TextDirectionality. This effectively tells the
        // directionality of the last strongly directional character
        // in the text.
        HBufC* temp = aText.Left( fits ).AllocL();
        TPtr tempPtr = temp->Des();

        TInt i = 0;
        TInt j = tempPtr.Length() - 1;
        while ( i < j )
            {
            TText t = tempPtr[i];
            tempPtr[i++] = tempPtr[j];
            tempPtr[j--] = t;
            }
        
        TBidiText::TDirectionality dir = TBidiText::TextDirectionality( tempPtr );

        delete temp;

        TText dirMarker = ( dir == TBidiText::ERightToLeft ) ? KRLMarker : 
                                                               KLRMarker;
        
        // Insert zero width joiner if necessary to get the correct glyph form
        // before truncation.
        TText lastChar = aText[fits - 1];
        TText next = aText[fits];
        
        TInt ellipsisPos = fits;
        
        if ( CFont::CharactersJoin( lastChar, KZeroWidthJoiner ) &&
             CFont::CharactersJoin( lastChar, next ) )
            {
            aText[fits] = KZeroWidthJoiner;
            ellipsisPos++;
            }
            
        aText.SetMax();
        
        aText[ellipsisPos] = KEllipsis;    

        // Inserting direction marker after the ellipsis ensures that
        // the ellipsis is shown on the correct side of the text
        // before it.
        aText[ellipsisPos + 1] = dirMarker;

        aText.SetLength( ellipsisPos + 2 );
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::UpdateFocusL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::UpdateFocusL()
    {
    TCharFormat charFormat;
    TCharFormatMask applyMask;

    iEditor->RichText()->GetCharFormat( charFormat,
                                        applyMask,
                                        0,
                                        iEditor->TextLength() );
            
    if ( !IsFocused() )
        {
        TRgb textColor( KRgbBlack );
        AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), 
                                   textColor, 
                                   KAknsIIDQsnTextColors, 
                                   EAknsCIQsnTextColorsCG6 );
                                   
        charFormat.iFontPresentation.iTextColor = textColor;
        applyMask.SetAttrib( EAttColor );               
        
        charFormat.iFontPresentation.iHighlightStyle = TFontPresentation::EFontHighlightNone;
        applyMask.SetAttrib( EAttFontHighlightStyle );
        }
    else
        {
        TRgb textColor( KRgbWhite );
        AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), 
                                   textColor, 
                                   KAknsIIDQsnHighlightColors, 
                                   EAknsCIQsnTextColorsCG3 );
                                   
        charFormat.iFontPresentation.iTextColor = textColor;
        applyMask.SetAttrib( EAttColor );               
           
        TRgb highlightColor( KRgbBlue );
        AknsUtils::GetCachedColor( AknsUtils::SkinInstance(), 
                                   highlightColor, 
                                   KAknsIIDQsnHighlightColors, 
                                   EAknsCIQsnHighlightColorsCG1 );
                                   
        charFormat.iFontPresentation.iHighlightColor = highlightColor;
        applyMask.SetAttrib( EAttFontHighlightColor );

        charFormat.iFontPresentation.iHighlightStyle = TFontPresentation::EFontHighlightNormal;
        applyMask.SetAttrib( EAttFontHighlightStyle );
        }
    
    iEditor->RichText()->ApplyCharFormatL( charFormat,
                                           applyMask,
                                           0,
                                           iEditor->TextLength() );    
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::DoHandleResourceChangeBeforeL
//
// Contains functionality that base class handle resource change 
// is depending on to be completed before it can be executed properly.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::DoHandleResourceChangeBeforeL( TInt aType )
    {
    switch( aType )
        {
        case KEikDynamicLayoutVariantSwitch:
            {
#ifdef RD_SCALABLE_UI_V2
            if ( AknLayoutUtils::PenEnabled() )
                {
                if ( !iButton )
                    {
                    CreateButtonL();
                    }
                }
            else
                {
                delete iButton;
                iButton = NULL;
                }
#endif // RD_SCALABLE_UI_V2
            break;
            }
        default:
            {
            break;
            }
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::DoHandleResourceChangeAfterL
//
// Contains functionality that is dependant of base class handle
// resource change to complete before these can be executed properly.
// ---------------------------------------------------------
//
void CMsgAttachmentControl::DoHandleResourceChangeAfterL( TInt aType )
    {
    switch( aType )
        {
        case KEikDynamicLayoutVariantSwitch:
            {
            StopAutomaticScrolling();
            iScrollPos = EScrollPosBeginning;
            SetTextForEditorL( EFalse, ETrue );  
            
            break;
            }
        case KAknsMessageSkinChange:
            {
            CGulIcon* icon = CreateAttachmentIconsL();
            CleanupStack::PushL( icon );
            
            if ( iButton )
                {
                CGulIcon* buttonIcon = CGulIcon::NewL( icon->Bitmap(), icon->Mask() );
                buttonIcon->SetBitmapsOwnedExternally( ETrue ); // bitmaps owned by iLabelIcon
    
                iButton->State()->SetIcon( buttonIcon ); // buttonIcon ownership transferred
                }
            
            // Make the change here as button will take the old size from
            // old icons that must be still valid when setting to button icon.
            delete iLabelIcon;
            iLabelIcon = icon;
            
            CleanupStack::Pop( icon );
            
            ResolveLayoutsL();
            break;
            }
        default:
            {
            break;
            }
        }
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::AddToAttachmentArrayL
// ---------------------------------------------------------
//
void CMsgAttachmentControl::AddToAttachmentArrayL( const TDesC& aName, TInt aIndex )
    {
    HBufC* convertedFileName = 
                AknTextUtils::ConvertFileNameL( aName );
    CleanupStack::PushL( convertedFileName );
    
    TPtr convertedFileNamePtr = convertedFileName->Des();
    
    AknTextUtils::StripCharacters( convertedFileNamePtr, KStripList );
    
    iAttachmentArray->InsertL( aIndex, *convertedFileName );
    
    CleanupStack::PopAndDestroy( convertedFileName );
    }

// ---------------------------------------------------------
// CMsgAttachmentControl::LayoutButton
// ---------------------------------------------------------
//    
void CMsgAttachmentControl::LayoutButton()
    {
#ifdef RD_SCALABLE_UI_V2
    CMsgExpandableControl::LayoutButton();
    
    if ( iButton )
        {
        TMargins8 margins;
        TRect buttonRect = iButtonLayout.Rect();
        TRect iconRect = iIconLayout.Rect();
        margins.iTop = iconRect.iTl.iY - buttonRect.iTl.iY;
        margins.iBottom = buttonRect.iBr.iY - iconRect.iBr.iY;
        margins.iLeft = iconRect.iTl.iX - buttonRect.iTl.iX;
        margins.iRight = buttonRect.iBr.iX - iconRect.iBr.iX;
        
        iButton->SetMargins( margins );
        }
#endif // RD_SCALABLE_UI_V2
    }


//  End of File
