/*
* Copyright (c) 2007 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:  MsgExpandableTextEditorControl implementation
*
*/



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

#include <eikenv.h>                        // for CEikonEnv
#include <eikedwin.h>                      // for TClipboardFunc
#include <txtrich.h>                       // for CRichText
#include <AknUtils.h>                      // for AknUtils
#include <aknenv.h>                        // for CAknEnv
#include <AknDef.h>


#include "MsgExpandableTextEditorControl.h"
#include "MsgEditorCommon.h"               //
#include "MsgExpandableControlEditor.h"    // for CMsgExpandableControlEditor
#include "MsgBaseControlObserver.h"        // for MMsgBaseControlObserver
#include "MsgEditorPanic.h"                // for CMsgEditor panics
#include "MsgEditorLogging.h"

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

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

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

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

// ========== LOCAL CONSTANTS AND MACROS ===================
const TInt KCursorPosNotSet = -1;

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

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

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

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

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::CMsgExpandableTextEditorControl
//
// Constructor.
// ---------------------------------------------------------
//
EXPORT_C CMsgExpandableTextEditorControl::CMsgExpandableTextEditorControl() :
    iCursorPos( KCursorPosNotSet )
    {
    SetComponentsToInheritVisibility( ETrue );
    }

// ---------------------------------------------------------
// CMsgExpandableControl::CMsgExpandableControl
//
// Constructor.
// ---------------------------------------------------------
//
EXPORT_C CMsgExpandableTextEditorControl::CMsgExpandableTextEditorControl( MMsgBaseControlObserver& aBaseControlObserver ) :
    CMsgBaseControl( aBaseControlObserver ),
    iCursorPos( KCursorPosNotSet )
    {
    SetComponentsToInheritVisibility( ETrue );
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::~CMsgExpandableTextEditorControl
//
// Destructor.
// ---------------------------------------------------------
//
EXPORT_C CMsgExpandableTextEditorControl::~CMsgExpandableTextEditorControl()
    {
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::SetPlainTextMode
//
// Sets the plain text mode on or off.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::SetPlainTextMode( TBool aMode )
    {
    if ( aMode )
        {
        iControlModeFlags |= EMsgControlModePlainTextMode;
        }
    else
        {
        iControlModeFlags &= ~EMsgControlModePlainTextMode;
        }

    iEditor->SetAllowPictures( !( iControlModeFlags & EMsgControlModePlainTextMode ) );
    }

// ---------------------------------------------------------
// CMsgExpandableControl::IsPlainTextMode
//
// Checks if the plain text mode is on and returns ETrue if it is.
// ---------------------------------------------------------
//
EXPORT_C TBool CMsgExpandableTextEditorControl::IsPlainTextMode() const
    {
    return iControlModeFlags & EMsgControlModePlainTextMode;
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::TextContent
//
// Returns a reference to text content of the editor.
// ---------------------------------------------------------
//
EXPORT_C CRichText& CMsgExpandableTextEditorControl::TextContent() const
    {
    return *iEditor->RichText();
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::CopyDocumentContentL
//
// Copies aInText to aOutText.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::CopyDocumentContentL( CGlobalText& aInText,
                                                                     CGlobalText& aOutText )
    {
    iEditor->CopyDocumentContentL( aInText, aOutText );
    iControlModeFlags |= EMsgControlModeModified;
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::SetMaxNumberOfChars
//
// Sets maximun number of characters.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::SetMaxNumberOfChars( TInt aMaxNumberOfChars )
    {
    iEditor->SetMaxNumberOfChars( aMaxNumberOfChars );
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::SetCursorPosL
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::SetCursorPosL( TInt aCursorPos )
    {
    if ( iEditor &&
         IsActivated() &&
         ( iEditor->CursorPos() != aCursorPos || 
           !iEditor->IsCursorVisibleL() ) )
        {
        iEditor->SetCursorPosL( aCursorPos, EFalse );
        iBaseControlObserver->HandleBaseControlEventRequestL( this, 
                                                              EMsgEnsureCorrectFormPosition, 
                                                              0 );            
        }
    iCursorPos = aCursorPos;
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::ActivateL
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::ActivateL()
    {
    CMsgBaseControl::ActivateL();
    
    if ( iCursorPos != KCursorPosNotSet )
        {
        SetCursorPosL( iCursorPos );
        }
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::ScrollL
//
// Tries to scroll the given amount of pixels. Restrict scroll
// to top of lines is switched off to allow better scrolling of
// text view. Otherwise scrolling would be limited to top of 
// the text lines. Margin pixels are scrolled "away" when control reaches
// the end.
// ---------------------------------------------------------
//
#ifdef RD_SCALABLE_UI_V2
EXPORT_C TInt CMsgExpandableTextEditorControl::ScrollL( TInt aPixelsToScroll, TMsgScrollDirection aDirection )
    {
    TInt pixelsToScroll( 0 );
    
    CTextLayout* textLayout = iEditor->TextLayout();
    
    if ( aDirection == EMsgScrollDown )
        {
        // Negative pixels means scrolling down.
        pixelsToScroll = -aPixelsToScroll;
        }
    else
        {
        // Positive pixels means scrolling up.
        pixelsToScroll = aPixelsToScroll;
        }
    
    if ( pixelsToScroll != 0 )
        {
        iEditor->TextView()->ScrollDisplayPixelsL( pixelsToScroll );
                
        if ( Abs( pixelsToScroll ) % iLineHeight != 0 )
            {
            pixelsToScroll = Abs( pixelsToScroll ) - ( Size().iHeight - iEditor->Size().iHeight );
            }

        }
    
    return Abs( pixelsToScroll );
    }
#else
EXPORT_C TInt CMsgExpandableTextEditorControl::ScrollL( TInt /*aPixelsToScroll*/, TMsgScrollDirection /*aDirection*/ )
    {
    return 0;
    }
#endif // RD_SCALABLE_UI_V2

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::CurrentLineRect
//
// Returns the current control rect.
// ---------------------------------------------------------
//
EXPORT_C TRect CMsgExpandableTextEditorControl::CurrentLineRect()
    {
    TRect lineRect( 0, 0, 0, 0 );
    TRAPD( ret, lineRect = iEditor->CurrentLineRectL() );
    if ( ret != KErrNone )
        {
        return lineRect;
        }

    TRect ctrlRect( Rect() );

    lineRect.iTl.iX = ctrlRect.iTl.iX;
    lineRect.iBr.iX = ctrlRect.iBr.iX;
    // "lineRect" is relative to "ctrlRect" -> Move needed
    lineRect.Move( TPoint( 0, iPosition.iY  ) );

    // "lineRect" should never be outside the "ctrlRect"
    // There seems to be a bug in Edwin when layouting is
    // changed from "full" to "partial". In this case the
    // "lineRect" might get outside "ctrlRect". This
    // causes false alarm and unwanted scrolling in
    // CMsgEditorView::EnsureCorrectFormPosition.
    // -> Has to make sure lineRect won't get outside ctrlRect

    if ( lineRect.iBr.iY > ctrlRect.iBr.iY )
        {
        lineRect.Move( TPoint( 0, ctrlRect.iBr.iY - lineRect.iBr.iY ) );
        }

    return lineRect;
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::SetBaseControlObserver
//
// Sets base control observer.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::SetBaseControlObserver( MMsgBaseControlObserver& aBaseControlObserver )
    {
    CMsgBaseControl::SetBaseControlObserver( aBaseControlObserver );
    iEditor->SetBaseControlObserver( aBaseControlObserver );
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::ClipboardL
//
// Handles clipboard operation.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::ClipboardL( TMsgClipboardFunc /*aFunc*/ )
    {
    // changed to do nothing because
    // CMsgExpandableControlEditor::Ccpu???? functions handle
    // clipboard operations now.
    }
// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::EditL
//
// Handles editing operation.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::EditL( TMsgEditFunc aFunc )
    {
    TUint32 editPermission = EditPermission();

    switch ( aFunc )
        {
        case EMsgUndo:
            {
            if ( editPermission & EMsgEditUndo )
                {
                iEditor->UndoL();
                }
            break;
            }
        case EMsgSelectAll:
            {
            if ( editPermission & EMsgEditSelectAll )
                {
                iEditor->SelectAllL();
                HandleControlEventL( this, MCoeControlObserver::EEventStateChanged );
                }
            break;
            }
        default:
            {
            __ASSERT_DEBUG( EFalse, Panic( EMsgIncorrectEditFunction ) );
            break;
            }
        }
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::EditPermission
//
// Returns edit permission flags.
// ---------------------------------------------------------
//
EXPORT_C TUint32 CMsgExpandableTextEditorControl::EditPermission() const
    {
    return iEditor->CheckEditPermission();
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::IsCursorLocation
//
// Checks if the cursor location is on the topmost or downmost position
// depending on aLocation and returns ETrue if it is.
// ---------------------------------------------------------
//
EXPORT_C TBool CMsgExpandableTextEditorControl::IsCursorLocation( TMsgCursorLocation aLocation ) const
    {
    switch ( aLocation )
        {
        case EMsgTop:
            {
            return iEditor->CursorInFirstLine();
            }
        case EMsgBottom:
            {
            return iEditor->CursorInLastLine();
            }
        default:
            {
            return EFalse;
            }
        }
    }


// CMsgExpandableTextEditorControl::VirtualHeight
//
// Returns approximate height of the control.
// ---------------------------------------------------------
//
EXPORT_C TInt CMsgExpandableTextEditorControl::VirtualHeight()
    {
    return iEditor->VirtualHeight();
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::VirtualVisibleTop
//
// Returns a topmost visible text position. Queries the visible top
// from editor and adds control top position differences (= top margin ) 
// into it if control is not visible.
// ---------------------------------------------------------
//
EXPORT_C TInt CMsgExpandableTextEditorControl::VirtualVisibleTop()
    {
    return iEditor->VirtualVisibleTop();
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::HandleControlEventL
// 
// Handles state change event from text editor. Checks whether form 
// updating is needed. This is needed atleast when subject field
// is visible at the last row of the view and word is typed using T9 
// so that line is changed in the middle of it. Without this check
// the view is not updated correctly.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::HandleControlEventL( CCoeControl* aControl, 
                                                                    TCoeEvent aEventType )
    {
    if ( aControl == iEditor &&
         aEventType == MCoeControlObserver::EEventStateChanged )
        {
        iBaseControlObserver->HandleBaseControlEventRequestL( this, 
                                                              EMsgEnsureCorrectFormPosition, 
                                                              0 );
        iControlModeFlags |= EMsgControlModeModified;
        }
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::HandleEdwinSizeEventL
//
// Handles expandable text editor size event. These event are send only when
// the editor height changes. First desirable heigh is limited to maximum
// body height. This is needed as edwin will report values larger than 
// maximum height. After this a change delta between current and desired 
// heights is calculated. This is rounded to the next full baseline. 
// If editor height is wanted to reduce and there is still unshown content
// then the content is scrolled downward so that it becomes visible. In this
// case the editor or control height is not changed. If editor height is 
// wanted to increase or all the content is current shown then
// control and editor heights are changed. If body max height flag
// is specified for control then the control height is always set to maximum.
// After heights have been changed base control observer (CMsgEditorView)
// is notified.
// ---------------------------------------------------------
//
EXPORT_C TBool CMsgExpandableTextEditorControl::HandleEdwinSizeEventL( CEikEdwin* /*aEdwin*/,
                                                                       TEdwinSizeEvent aEventType,
                                                                       TSize aDesirableEdwinSize )
    {
    if ( aEventType == EEventSizeChanging )
        {
        MEBLOGGER_ENTERFN("CMsgExpandableTextEditorControl::HandleEdwinSizeEventL");
        
        aDesirableEdwinSize.iHeight = Min( aDesirableEdwinSize.iHeight, iMaxBodyHeight );
        
        TInt delta = aDesirableEdwinSize.iHeight - iEditor->Size().iHeight;
        MsgEditorCommons::RoundToNextLine( delta, iLineHeight );
        
        if ( delta < 0 && 
             iEditor->VirtualHeight() > iMaxBodyHeight )
            {
            ScrollL( delta, EMsgScrollDown );
            iBaseControlObserver->HandleBaseControlEventRequestL( this, 
                                                                  EMsgUpdateScrollbar, 
                                                                  delta );
            }        
        else if ( delta ||
                  iControlModeFlags & EMsgControlModeForceSizeUpdate )
            {
            if ( iControlModeFlags & EMsgControlModeBodyMaxHeight )
                {
                aDesirableEdwinSize.iHeight = iEditor->MaximumHeight();
                }            
            
            // Performs the real size change if height has really changed
            // or if forced size change is set.
            iSize.iHeight = aDesirableEdwinSize.iHeight;
            MsgEditorCommons::RoundToNextLine( iSize.iHeight, iLineHeight );
            
            if ( delta != 0 )
                {
                iEditor->SetAndGetSizeL( aDesirableEdwinSize );
                }

            iBaseControlObserver->HandleBaseControlEventRequestL( this, 
                                                                  EMsgHeightChanged, 
                                                                  delta );
            }

        MEBLOGGER_LEAVEFN("CMsgExpandableTextEditorControl::HandleEdwinSizeEventL");

        if ( delta < 0 )
            { 
            return ETrue;
            }
        }

    return EFalse;
    }

// ---------------------------------------------------------
// CMsgExpandableTextEditorControl::HandleEdwinEventL
//
// Handles events from expandable text editor. Navigation and format
// change events are handled.
// ---------------------------------------------------------
//
EXPORT_C void CMsgExpandableTextEditorControl::HandleEdwinEventL( CEikEdwin* aEdwin, 
                                                                  TEdwinEvent aEventType)
    {
    if ( aEventType == MEikEdwinObserver::EEventNavigation && aEdwin->TextView() )
        {
        // When cursor is in beginning of text and left key is pressed,
        // FEP places cursor to the end of text. This scrolls end of text
        // to be visible. With pen support this needs to be handled always since
        // select & drag scrolling might change the text position so that form updating
        // is needed.
        iBaseControlObserver->HandleBaseControlEventRequestL( this, 
                                                              EMsgEnsureCorrectFormPosition, 
                                                              0 );
        }
    else if ( aEventType == MEikEdwinObserver::EEventFormatChanged )
        {
        iBaseControlObserver->HandleBaseControlEventRequestL( this, 
                                                              EMsgEnsureCorrectFormPosition, 
                                                              0 );
        }
    }


//  End of File
