/*
* Copyright (c) 2002-2008 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:  Implementation of the decorator class for navigation
*                pane controls.
*
*/


// SYSTEM INCLUDE FILES
#include <AknsUtils.h>
#include <AknsDrawUtils.h>
#include <aknlayoutscalable_avkon.cdl.h>
#include <layoutmetadata.cdl.h>
#include <touchfeedback.h>

#include <AknTasHook.h>
// USER INCLUDE FILES
#include "aknappui.h"
#include "aknconsts.h"
#include "aknnavilabel.h"
#include "aknnavide.h"
#include "AknNaviObserver.h"
#include "AknUtils.h"
#include "AknStatuspaneUtils.h"
#include "AknNaviDecoratorObserver.h"
#include "akntabgrp.h"
#include "akntitle.h"
#include "AknTitlePaneLabel.h"


const TInt KEikNavidePointerRepeatInterval = 500000;  // in micro seconds


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

NONSHARABLE_CLASS( CAknNavigationDecoratorExtension ) : public CBase
    {
public:
    enum
        {
        ENaviDecoratorNoSide = 0,
        ENaviDecoratorLeftSide,
        ENaviDecoratorRightSide
        };
    
public:
    static CAknNavigationDecoratorExtension* NewL();
    ~CAknNavigationDecoratorExtension();
    
private:
    CAknNavigationDecoratorExtension();
    CAknNavigationDecoratorExtension( const CAknNavigationDecoratorExtension* aExtension );
    
public:
    CPeriodic* iTimer;
    TInt iEffectTimerCount;
    TInt iOutermostRepeats;
    TInt iBlinkingSide;
    };

CAknNavigationDecoratorExtension* CAknNavigationDecoratorExtension::NewL()
    {
    CAknNavigationDecoratorExtension* naviExtension = 
        new (ELeave) CAknNavigationDecoratorExtension();
    return naviExtension;
    }

CAknNavigationDecoratorExtension::CAknNavigationDecoratorExtension() : 
    iTimer(NULL), iEffectTimerCount(0), iOutermostRepeats(0), iBlinkingSide(0)
    {
    }

CAknNavigationDecoratorExtension::~CAknNavigationDecoratorExtension()
    {
    }
    
// -----------------------------------------------------------------------------
// IsMaskAllBlack
//
// Checks whether the given bitmap is all black (first row ignored).
// The width of the bitmap is assumed to be 8 or less (sufficient for arrows).
//
// Returns: Boolean value.
// -----------------------------------------------------------------------------
//
static TBool IsMaskAllBlack( CFbsBitmap* bitmap )
    {
    if ( !bitmap )
        {
        return EFalse;
        }

    TSize size( bitmap->SizeInPixels() );

    if ( size.iWidth > 8 )
        {
        return EFalse;
        }
        
    TBuf8<8> scanLineBuf;

    for ( TInt y = 1; y < size.iHeight; y++ )
        {
        bitmap->GetScanLine( scanLineBuf,
                             TPoint( 0, y ),
                             size.iWidth,
                             EGray256 );
        for ( TInt x = 0; x < size.iWidth; x++ )
            {
            if ( scanLineBuf[x] != 0 )
                {
                return EFalse;
                }
            }
        }

    return ETrue;
    }

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

EXPORT_C CAknNavigationDecorator* CAknNavigationDecorator::NewL(
    CAknNavigationControlContainer* /*aNavigationControlContainer*/,
    CCoeControl* aDecoratedControl,
    TControlType aType )
    {
    CleanupStack::PushL( aDecoratedControl );
    CAknNavigationDecorator* self = new (ELeave) CAknNavigationDecorator;

    self->iDecoratedControl = aDecoratedControl;

    CleanupStack::Pop( aDecoratedControl );
    
    // Control type must be set before calling constructor.
    self->iControlType = aType;
    
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self ); // self
    AKNTASHOOK_ADDL( self, "CAknNavigationDecorator" );
    return self;
    }

EXPORT_C CAknNavigationDecorator::~CAknNavigationDecorator()
    {
    AKNTASHOOK_REMOVE();
    if ( iContainer )
        {
        iContainer->Pop( this );
        }

    if ( iLayoutStyleTimer )
        {
        iLayoutStyleTimer->Cancel();
        delete iLayoutStyleTimer;
        }
        
    if( iExtension && iExtension->iTimer )
        {
        if ( iExtension->iTimer->IsActive() )
            {
            CancelTimer();
            }

        delete iExtension->iTimer;
        }
    
    delete iExtension;    
    delete iDecoratedControl;
    }


EXPORT_C void CAknNavigationDecorator::ConstructL()
    {
    iDecoratedControl->SetObserver( this );

    iNaviArrowsVisible    = EFalse;
    iNaviArrowLeftDimmed  = ETrue;
    iNaviArrowRightDimmed = ETrue;
    
    TBool isLandscape( Layout_Meta_Data::IsLandscapeOrientation() );
    
    iLayoutStyleTimer = CPeriodic::NewL( CActive::EPriorityIdle );
    if ( AknStatuspaneUtils::FlatLayoutActive() &&
         !isLandscape )
        {
        iLayoutFlags |= ENaviControlLayoutNarrow;
        iLayoutFlags |= ENaviControlLayoutModeForced;
        }

    // Wide layout is used in usual portrait mode,
    // except for tabs in non-touch layouts.
    else if ( AknStatuspaneUtils::UsualLayoutActive() &&
              !isLandscape &&
              ( iControlType != ETabGroup ||
                AknLayoutUtils::PenEnabled() ) )
        {
        iLayoutFlags |= ENaviControlLayoutWide;
        iLayoutFlags |= ENaviControlLayoutModeForced;
        }
    else
        {        
        iLayoutFlags |= ENaviControlLayoutNormal;
        iLayoutFlags |= ENaviControlLayoutModeAutomatic;
        }
    
    iExtension = CAknNavigationDecoratorExtension::NewL();
    
    ActivateL();
    }

EXPORT_C CCoeControl* CAknNavigationDecorator::DecoratedControl()
    {
    // In case the decorated control is of the ENaviVolume type, this
    // function returns the CAknVolumeControl object which is a component
    // control of the volume popup, instead of the volume popup control itself.
    // This is in order to maintain BC.
    if ( iControlType == ENaviVolume )
        {
        CCoeControl* volumeControl = iDecoratedControl->ComponentControl( 5 );
        
        if ( volumeControl )
            {
            return volumeControl;
            }
        }

    return iDecoratedControl;
    }

EXPORT_C void CAknNavigationDecorator::MakeScrollButtonVisible( TBool aVisible )
    {
    TBool differ = ( iNaviArrowsVisible != aVisible );
    
    iNaviArrowsVisible = aVisible;
    
    if ( iControlType == ENaviLabel )
        {
        if ( iNaviArrowsVisible )
            {
            STATIC_CAST( CAknNaviLabel*, iDecoratedControl )->
                SetNaviLabelType( CAknNaviLabel::ENavigationLabel );
            }
        else
            {
            STATIC_CAST( CAknNaviLabel*, iDecoratedControl )->
                SetNaviLabelType( CAknNaviLabel::EAdditionalInfoLabel );
            }
        }
    else if ( iControlType == ETabGroup )
        {
        static_cast<CAknTabGroup*>(
            iDecoratedControl )->SetNaviArrowsVisible( aVisible );
        }

    if ( differ )
        {
        SizeChanged();
        }
    }

EXPORT_C TBool CAknNavigationDecorator::ScrollButtonVisible() const
    {
    return iNaviArrowsVisible;
    }

EXPORT_C void CAknNavigationDecorator::SetScrollButtonDimmed(
    TScrollButton aButton,
    TBool aDimmed )
    {
    if ( aButton == ELeftButton )
        {
        iNaviArrowLeftDimmed = aDimmed;

        if ( aDimmed )
            {
            iExtension->iBlinkingSide = 
                CAknNavigationDecoratorExtension::ENaviDecoratorRightSide;
            }
        }
    if ( aButton == ERightButton )
        {
        iNaviArrowRightDimmed = aDimmed;

        if ( aDimmed )
            {
            iExtension->iBlinkingSide = 
                CAknNavigationDecoratorExtension::ENaviDecoratorLeftSide;
            }
        }
    }

EXPORT_C TBool CAknNavigationDecorator::IsScrollButtonDimmed(
    TScrollButton aButton ) const
    {
    if ( aButton == ELeftButton )
        {
        return iNaviArrowLeftDimmed;
        }
    if ( aButton == ERightButton )
        {
        return iNaviArrowRightDimmed;
        }
    return ETrue;
    }

EXPORT_C void CAknNavigationDecorator::SetControlType( TControlType aType )
    {
    iControlType = aType;
    }

EXPORT_C CAknNavigationDecorator::TControlType CAknNavigationDecorator::ControlType() const
    {
    return iControlType;
    }

EXPORT_C void CAknNavigationDecorator::SizeChanged()
    {
    if ( iControlType == ENaviVolume )
        {
        // The control for ENaviVolume type does not reside in the navi pane
        // anymore, so the control size doesn't need to be adjusted if navi pane
        // size changes.
        return;
        }
    
    TBool isLandscape( Layout_Meta_Data::IsLandscapeOrientation() );
    TBool flatLayout( AknStatuspaneUtils::FlatLayoutActive() );
    
    if ( isLandscape &&
         flatLayout &&
         !AknLayoutUtils::PenEnabled() &&
         NaviControlLayoutMode() == ENaviControlLayoutModeAutomatic &&
         NaviControlLayoutStyle() == ENaviControlLayoutNormal )
        {
        if ( iLayoutStyleTimer && !iLayoutStyleTimer->IsActive() )
            {
            InitLayoutStyleTimer();
            }            
        }
    else
        {
        CancelLayoutStyleTimer();
        }    
    
    TBool wideInUseInLsc( EFalse );
    
    if ( NaviControlLayoutStyle() != ENaviControlLayoutNarrow &&
         flatLayout &&
         !isLandscape )
        {
        iLayoutFlags &= ~ENaviControlLayoutNormal;
        iLayoutFlags &= ~ENaviControlLayoutWide;
        iLayoutFlags |= ENaviControlLayoutNarrow;
        }

    // Only flat layout supports narrow mode for now.
    if ( NaviControlLayoutStyle() == ENaviControlLayoutNarrow )
        {
        if ( !flatLayout ||
             ( isLandscape && AknLayoutUtils::PenEnabled() ) )
            {
            iLayoutFlags &= ~ENaviControlLayoutNarrow;           
            iLayoutFlags |= ENaviControlLayoutNormal; 
            }
        }
    // Wide layout is supported only in portrait mode.
    else if ( NaviControlLayoutStyle() == ENaviControlLayoutWide )
        {
        if ( Layout_Meta_Data::IsLandscapeOrientation() )
            {
            wideInUseInLsc = ETrue;
            }
        }
    
    if ( NaviControlLayoutStyle() == ENaviControlLayoutNarrow &&
         NaviControlLayoutStyleSupported( ENaviControlLayoutNarrow ) )
        {
        SizeChangedInNarrowLayout();
        }
    else if ( NaviControlLayoutStyle() == ENaviControlLayoutWide &&
              !wideInUseInLsc )
        {
        SizeChangedInWideLayout();
        }
    else // Normal layout
        {
        SizeChangedInNormalLayout();
        }

    // Finally call the navi arrow bitmap getter, as it will
    // resize the navi arrows to correct size.
    if ( iContainer )
        {
        iContainer->NaviArrowBitmap( 0 );
        iContainer->NaviArrowBitmap( 1 );
        }
    }

void CAknNavigationDecorator::SizeChangedInNormalLayout()
    {
    TRect rect;
    TRect leftArrowRect;
    TRect rightArrowRect;
    
    CEikStatusPaneBase* statusPane = CEikStatusPaneBase::Current();
    if ( statusPane && 
         statusPane->CurrentLayoutResId() == R_AVKON_STATUS_PANE_LAYOUT_USUAL_EXT &&
         iControlType == ETabGroup )
        {
        // At the moment this special handling is required for tab group
        // in portrait mode, because tabs don't support wide navi pane layout yet.
        TAknLayoutRect layoutRect;
            
        TRect screenRect;
        AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EScreen, screenRect );
        
        TBool isHDLayoutActive( AknStatuspaneUtils::HDLayoutActive() );
                    
        TAknWindowComponentLayout topArea( 
            AknLayoutScalable_Avkon::area_top_pane( isHDLayoutActive ? 8 : 0 ) );
        TAknWindowComponentLayout spLayout(
            AknLayoutScalable_Avkon::status_pane( isHDLayoutActive ? 1 : 0 ) );
            
        TAknWindowComponentLayout layout(
                TAknWindowComponentLayout::Compose( topArea, spLayout ) );
                
        layoutRect.LayoutRect( screenRect, layout );
        TRect spRect( layoutRect.Rect() );
                
        layoutRect.LayoutRect(
            spRect,
            AknLayoutScalable_Avkon::navi_pane( isHDLayoutActive ? 8 : 5 ) );
        rect = layoutRect.Rect();
        rect.Move( -rect.iTl.iX, -rect.iTl.iY );
        leftArrowRect  = NaviArrowRect( ELeftButton,  EFalse, rect );
        rightArrowRect = NaviArrowRect( ERightButton, EFalse, rect );
        }
    else
        {
        rect = ParentRect();
        
        if ( iControlType == ENaviVolume &&
             !Layout_Meta_Data::IsLandscapeOrientation() &&
             AknStatuspaneUtils::SmallLayoutActive() )
            {
            // Small status pane volume control has it's own positions for
            // navi arrows, but in landscape the data doesn't exist
            // so normal navi arrow positions are used.
            
            // These layout data items aren't mirrored.
            TBool layoutMirrored( AknLayoutUtils::LayoutMirrored() );
            
            TRect naviPaneRect;
            AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ENaviPane,
                                               naviPaneRect );
                                               
            naviPaneRect.Move( -naviPaneRect.iTl.iX,
                               -naviPaneRect.iTl.iY );
            
            TAknLayoutRect layoutRect;
            layoutRect.LayoutRect(
                naviPaneRect,
                layoutMirrored ?
                    AknLayoutScalable_Avkon::status_small_pane_g6( 0 ) :
                    AknLayoutScalable_Avkon::status_small_pane_g5( 0 ) );
            leftArrowRect = layoutRect.Rect();

            layoutRect.LayoutRect(
                naviPaneRect,
                layoutMirrored ?
                    AknLayoutScalable_Avkon::status_small_pane_g5( 0 ) :
                    AknLayoutScalable_Avkon::status_small_pane_g6( 0 ) );
            rightArrowRect = layoutRect.Rect();   
            }
        else
            {
            leftArrowRect = NaviArrowRect( ELeftButton );
            rightArrowRect = NaviArrowRect( ERightButton );
            }
        }

    if ( iControlType == ETabGroup )
        {
        rect = DecoratedTabControlRect( AknLayoutUtils::PenEnabled(),
                                        ETrue );
        }

    iArrowRightSize = rightArrowRect.Size();
    iArrowLeftSize  = leftArrowRect.Size();
    
    iArrowRightPos = TPoint( rightArrowRect.iTl.iX, rect.iTl.iY );
    iArrowLeftPos  = TPoint( leftArrowRect.iTl.iX, rect.iTl.iY );
    
    TSize rectSize( rect.Size() );
    
    // In case the decorated control is tab group, the tab layout mode
    // must be set before it's size is set.
    if ( iControlType == ETabGroup )
        {
        CAknTabGroup* tabGroup = static_cast <CAknTabGroup*> (iDecoratedControl);
        TRAP_IGNORE( tabGroup->SetNarrowTabLayoutL( EFalse ) );
        }
    
    if ( ScrollButtonVisible() )
        {
        iDecoratedControl->SetRect( rect );
        }
    else
        {
        iDecoratedControl->SetRect( Rect() );
        }
    }


void CAknNavigationDecorator::SizeChangedInNarrowLayout()
    {
    TRect rect( DecoratedControlNarrowRect( this->ControlType() ) );
    
    TRect leftArrowRect( NaviArrowRect( ELeftButton, ETrue ) );
    TRect rightArrowRect( NaviArrowRect( ERightButton, ETrue ) );

    iArrowRightSize = rightArrowRect.Size();
    iArrowRightPos  = rightArrowRect.iTl;
    
    iArrowLeftSize  = leftArrowRect.Size();
    iArrowLeftPos   = leftArrowRect.iTl;

    // Tab group is handled specificly... 
    if ( iControlType == ETabGroup )
        {
        // Note. Size is set bigger than normally. 
        // Tabs take this into account in layout calculations (akntabgrp.cpp).
        iDecoratedControl->SetRect( Rect() ); 
        CAknTabGroup* tabGroup = static_cast <CAknTabGroup*> (iDecoratedControl);
        // This sets tab size using animation.
        TBool useAnimation( Layout_Meta_Data::IsLandscapeOrientation() );
        TRAP_IGNORE( tabGroup->SetNarrowTabLayoutL( ETrue, useAnimation ) );
        }
    else
        {
        if ( ScrollButtonVisible() )
            {
            iDecoratedControl->SetRect( TRect( iArrowLeftPos.iX + iArrowLeftSize.iWidth,
                                               rect.iTl.iY,
                                               iArrowRightPos.iX,
                                               rect.iBr.iY ) );
            }
        else
            {
            iDecoratedControl->SetRect( rect );
            }
        }                 
    }


void CAknNavigationDecorator::SizeChangedInWideLayout()
    {
    TRect rect( Rect() );
    TRect parentRect;

    if ( iControlType == ETabGroup &&
         !AknLayoutUtils::PenEnabled() )
        {
        parentRect = DecoratedTabControlRect( EFalse, ETrue );
        }
    else
        {
        parentRect = ParentRect();
        }

    if ( rect.Width()  < parentRect.Width() ||
         rect.Height() < parentRect.Height() )
        {
        rect = parentRect;
        }

    TRect leftArrowRect( NaviArrowRect( ELeftButton ) );
    TRect rightArrowRect( NaviArrowRect( ERightButton ) );

    iArrowRightSize = rightArrowRect.Size();
    iArrowLeftSize  = leftArrowRect.Size();

    if ( ScrollButtonVisible() )
        {
        rect.iTl.iX += iArrowRightSize.iWidth;
        rect.iBr.iX -= iArrowLeftSize.iWidth;
        }
    
    if ( parentRect.Height() < rect.Height() )
        {
        iArrowRightPos = TPoint( rect.iBr.iX, parentRect.iTl.iY );
        iArrowLeftPos = TPoint( rect.iTl.iX - iArrowLeftSize.iWidth, parentRect.iTl.iY );
        }
    else
        {
        iArrowRightPos = TPoint( rect.iBr.iX, rightArrowRect.iTl.iY );
        iArrowLeftPos = TPoint( rect.iTl.iX - iArrowLeftSize.iWidth, iArrowRightPos.iY );
        }

    // In case the decorated control is tab group, the tab layout mode
    // must be set before it's size is set.
    if ( iControlType == ETabGroup )
        {
        CAknTabGroup* tabGroup = static_cast <CAknTabGroup*> ( iDecoratedControl );
        TRAP_IGNORE( tabGroup->SetNarrowTabLayoutL( EFalse) );
        }

    iDecoratedControl->SetRect( rect );
    }

EXPORT_C TInt CAknNavigationDecorator::CountComponentControls() const
    {
    if ( iContainer && iDecoratedControl )
        {
        return 1;
        }

    return 0;
    }


EXPORT_C CCoeControl* CAknNavigationDecorator::ComponentControl(TInt /*aIndex*/) const
    {
    // DecoratedControl() isn't declared as const but doesn't actually
    // change anything.
    return ( const_cast<CAknNavigationDecorator*>(this)->DecoratedControl() );
    }


EXPORT_C void* CAknNavigationDecorator::ExtensionInterface( TUid /*aInterface*/ ) 
    { 
    return NULL;
    }

   
// ---------------------------------------------------------------------------
// CAknNavigationDecorator::HandlePointerEventL
// Handles pointer events by checking first if the pointer event was
// in the arrow areas and sending the arrow event to observer,
// and if not sending the pointer event to all child components.
// ---------------------------------------------------------------------------
//
EXPORT_C void CAknNavigationDecorator::HandlePointerEventL(
    const TPointerEvent& aPointerEvent )
    {
    if ( AknLayoutUtils::PenEnabled() )
        {
        TRect naviPaneRect( ParentRect() );
        TRect parentRect = naviPaneRect;
        
        TRect statusPaneRect;
        AknLayoutUtils::LayoutMetricsRect(
            AknLayoutUtils::EStatusPane, statusPaneRect );
        
        // In certain status pane layouts the navi pane has extended
        // touch responsive area in the layout data which is larger
        // than the actual decorator size.
        CEikStatusPaneBase* statusPane = CEikStatusPaneBase::Current();
        if ( statusPane )
            {
            TInt currentLayoutResId( statusPane->CurrentLayoutResId() );
            
            if ( currentLayoutResId == R_AVKON_WIDESCREEN_PANE_LAYOUT_USUAL ||
                 currentLayoutResId == R_AVKON_WIDESCREEN_PANE_LAYOUT_IDLE )
                {
                TAknLayoutRect layoutRect;
                layoutRect.LayoutRect(
                    statusPaneRect,
                    AknLayoutScalable_Avkon::aid_touch_navi_pane() );
                parentRect = layoutRect.Rect();
                
                // Make the parent rect navi pane relative and move it so
                // that the bottom sides of the real navi pane area and the
                // touch pane responsive area are aligned. This means
                // that the iTl.iY will become negative, which is required
                // for pointer events in which the down event happens
                // inside the decorator rect, but the up event in the
                // extended area above the decorator rect.
                parentRect.Move( -parentRect.iTl.iX,
                                 -( parentRect.iTl.iY +
                                    ( parentRect.Height() -
                                      naviPaneRect.Height() ) ) );
                }
            }

        TRect rightArrowTabRect( iArrowRightPos, iArrowRightSize );
        TRect leftArrowTabRect( iArrowLeftPos, iArrowLeftSize );
        
        // Get tab arrow touch aid rects from layout data, they extend the
        // arrow areas bit to the decorated control's area to make the arrows
        // more "finger usable".
        TAknLayoutRect arrowAidLayout;
        arrowAidLayout.LayoutRect(
            statusPaneRect,
            AknLayoutScalable_Avkon::aid_touch_tab_arrow_left(
                Layout_Meta_Data::IsLandscapeOrientation() ? 2 : 0 ) );
        
        // The arrows are the same size so use only the other's width.
        TInt aidWidth( arrowAidLayout.Rect().Width() );
        
        rightArrowTabRect.iTl.iX -= aidWidth;
        leftArrowTabRect.iBr.iX  += aidWidth;
                
        TBool rightArrowTapped(
            iNaviArrowsVisible &&
            !iNaviArrowRightDimmed &&
            rightArrowTabRect.Contains( aPointerEvent.iPosition ) );
            
        TBool leftArrowTapped(
            iNaviArrowsVisible &&
            !iNaviArrowLeftDimmed &&
            leftArrowTabRect.Contains( aPointerEvent.iPosition ) );
        
        // Inform controls if tapped to arrows.
        if ( aPointerEvent.iType == TPointerEvent::EButton1Down ||
             aPointerEvent.iType == TPointerEvent::EButtonRepeat )
            {
            // Detects whether tap hits left arrow.
            if ( leftArrowTapped && iNaviDecoratorObserver )
                {
                MTouchFeedback* feedback = MTouchFeedback::Instance();
                if ( feedback &&
                     ( iDecoratedControl && !iDecoratedControl->IsDimmed() ) )
                    {
                    TTouchLogicalFeedback feedbackType =
                        ( aPointerEvent.iType == TPointerEvent::EButtonRepeat ?
                              ETouchFeedbackSensitiveButton :
                              ETouchFeedbackBasicButton );
                    feedback->InstantFeedback( this, feedbackType, aPointerEvent );
                    }

                iNaviDecoratorObserver->HandleNaviDecoratorEventL(
                    MAknNaviDecoratorObserver::EAknNaviDecoratorEventLeftTabArrow );

                // Request pointer repeat to implement longtapping
                Window().RequestPointerRepeatEvent(
                    KEikNavidePointerRepeatInterval, leftArrowTabRect );

                // Don't pass the event to children as the tap point
                // may be in the decorated control's area and it's
                // already handled here.
                }
            // Detects whether tap hits right arrow.
            else if ( rightArrowTapped && iNaviDecoratorObserver )
                {
                MTouchFeedback* feedback = MTouchFeedback::Instance();
                if ( feedback &&
                     ( iDecoratedControl && !iDecoratedControl->IsDimmed() ) )
                    {
                    TTouchLogicalFeedback feedbackType =
                        ( aPointerEvent.iType == TPointerEvent::EButtonRepeat ?
                              ETouchFeedbackSensitiveButton :
                              ETouchFeedbackBasicButton );
                    feedback->InstantFeedback( this, feedbackType, aPointerEvent );
                    }

                iNaviDecoratorObserver->HandleNaviDecoratorEventL(
                    MAknNaviDecoratorObserver::EAknNaviDecoratorEventRightTabArrow );

                // Request pointer repeat to implement longtapping
                Window().RequestPointerRepeatEvent(
                    KEikNavidePointerRepeatInterval, rightArrowTabRect );

                // Don't pass the event to children as the tap point
                // may be in the decorated control's area and it's
                // already handled here.
                }
            else
                {
                // Pass the pointer events to children.
                CCoeControl::HandlePointerEventL( aPointerEvent );
                }

            // check if navi label was hit
            }
        else if ( aPointerEvent.iType == TPointerEvent::EButton1Up )
            {
            if ( iControlType == ENaviLabel )
                {
                if ( parentRect.Contains( aPointerEvent.iPosition ) &&
                     iNaviDecoratorObserver )
                    {
                    iNaviDecoratorObserver->HandleNaviDecoratorEventL(
                        MAknNaviDecoratorObserver::EAknNaviDecoratorEventNaviLabel );
                    }
                }
            else if( !rightArrowTapped && !leftArrowTapped )
                {
                CCoeControl::HandlePointerEventL( aPointerEvent );
                }
            }
        else
            {
            if ( !rightArrowTapped && !leftArrowTapped )
                {
                // Pass other pointer events to children if it
                // happened outside the arrow areas.
                CCoeControl::HandlePointerEventL( aPointerEvent );
                }
            }
        }
    }


EXPORT_C void CAknNavigationDecorator::HandleControlEventL(
    CCoeControl* /*aControl*/, TCoeEvent aEventType )
    {
    TInt event = aEventType;
    switch ( event )
        {
        case MAknNavigationObserver::ENaviEventHandleNavigation:
            {
            iNaviArrowRightDimmed = EFalse;
            iNaviArrowLeftDimmed  = EFalse;
            break;
            }
        case MAknNavigationObserver::ENaviEventRightMostItemReached:
            {
            iNaviArrowRightDimmed = ETrue;
            iNaviArrowLeftDimmed  = EFalse;
            break;
            }
        case MAknNavigationObserver::ENaviEventLeftMostItemReached:
            {
            iNaviArrowRightDimmed = EFalse;
            iNaviArrowLeftDimmed  = ETrue;
            break;
            }
        case MAknNavigationObserver::ENaviEventOneItemExists:
            {
            iNaviArrowRightDimmed = ETrue;
            iNaviArrowLeftDimmed  = ETrue;
            break;
            }
        case MAknNavigationObserver::ENaviEventAlreadyLeftmostItem:
            {
#ifdef RD_ANIMATION_EFFECTS 
            iExtension->iBlinkingSide = 
                    CAknNavigationDecoratorExtension::ENaviDecoratorRightSide;           
            StartTimerL();
            return;
#else
            break;
#endif // RD_ANIMATION_EFFECTS
            }
        case MAknNavigationObserver::ENaviEventAlreadyRightmostItem:
            {
#ifdef RD_ANIMATION_EFFECTS
            iExtension->iBlinkingSide = 
                    CAknNavigationDecoratorExtension::ENaviDecoratorLeftSide;
            StartTimerL();
            return;
#else // RD_ANIMATION_EFFECTS
            break;
#endif
            }
        case MAknNavigationObserver::ENaviEventRedrawNeeded:
            break;
        default:
            break;
        }
    
    ReportEventL( MCoeControlObserver::EEventStateChanged );
    
    MCoeControlObserver* observer = Observer();
    // If event was not sent yet to the navigation control container, send it now
    if ( iContainer && observer != iContainer )
        {
        SetObserver( iContainer );
        ReportEventL( MCoeControlObserver::EEventStateChanged );
        SetObserver( observer );
        }
    }


EXPORT_C void CAknNavigationDecorator::Draw( const TRect& /*aRect*/ ) const
    {
    // Draw navigation pane scroll indicator arrows if visible
    if ( iNaviArrowsVisible && iContainer )
        {
        CWindowGc& gc = SystemGc();
        
        CFbsBitmap* colorbmp = iContainer->NaviColorBitmap();

        if ( !iNaviArrowLeftDimmed )
            {
            CFbsBitmap* arrowBmp = &( iContainer->NaviArrowBitmap( 0 ) );
            CFbsBitmap* arrowMask = &( iContainer->NaviArrowBitmap( 1 ) );
            
            TPoint arrowPos( iArrowLeftPos );
            
            if ( arrowBmp )
                {
                TInt arrowHeight( arrowBmp->SizeInPixels().iHeight );
                if ( arrowHeight < Size().iHeight )
                    {
                    arrowPos.iY = ( Size().iHeight - arrowHeight ) / 2;
                    }
                }
                    
            if( colorbmp && !IsMaskAllBlack( arrowMask ) )
                {
                gc.BitBltMasked(
                    arrowPos,
                    colorbmp, 
                    TRect( iArrowLeftSize ),
                    arrowMask,
                    ETrue );
                }
            else
                {
                gc.BitBltMasked(
                    arrowPos,
                    arrowBmp, 
                    TRect( iArrowLeftSize ),
                    arrowMask,
                    ETrue );
                }
            }
        
        if ( !iNaviArrowRightDimmed )
            {
            CFbsBitmap* arrowBmp = &( iContainer->NaviArrowBitmap( 2 ) );
            CFbsBitmap* arrowMask = &( iContainer->NaviArrowBitmap( 3 ) );
            
            TPoint arrowPos( iArrowRightPos );
            
            if ( arrowBmp )
                {
                TInt arrowHeight( arrowBmp->SizeInPixels().iHeight );
                if ( arrowHeight < Size().iHeight )
                    {
                    arrowPos.iY = ( Size().iHeight - arrowHeight ) / 2;
                    }
                }
            
            if( colorbmp && !IsMaskAllBlack( arrowMask ) )
                {
                gc.BitBltMasked(
                    arrowPos,
                    colorbmp, 
                    TRect( iArrowRightSize ),
                    arrowMask,
                    ETrue );
                }
            else
                {
                gc.BitBltMasked(
                    arrowPos,
                    arrowBmp,
                    TRect( iArrowRightSize ),
                    arrowMask,
                    ETrue );
                }
            }
        }
    }

void CAknNavigationDecorator::SetNaviStack(
    CAknNavigationControlContainer* aContainer )
    {
    iContainer = aContainer;

    if ( iContainer &&
         iControlType == ETabGroup )
        {
        CAknTabGroup* tabGroup =
            static_cast <CAknTabGroup*> ( iDecoratedControl );
        tabGroup->SetDecoratorLayout( iLayoutFlags & ENaviControlLayoutNarrow );
        }
    }

// ---------------------------------------------------------------------------
// CAknNavigationDecorator::SetNaviDecoratorObserver
// Sets observer for navidecorator.
// ---------------------------------------------------------------------------
//  
EXPORT_C void CAknNavigationDecorator::SetNaviDecoratorObserver(
    MAknNaviDecoratorObserver* aObserver )
    {
    if ( AknLayoutUtils::PenEnabled() )
        {
        iNaviDecoratorObserver = aObserver;
        }
    }


// ---------------------------------------------------------------------------
// CAknNavigationDecorator::ParentRect
// Returns the navigation decorator rectangle.
// ---------------------------------------------------------------------------
//
TRect CAknNavigationDecorator::ParentRect()
    {
    return DecoratedControlRect( iControlType, iNaviArrowsVisible );
    }


// ---------------------------------------------------------------------------
// CAknNavigationDecorator::DecoratedControlNarrowRect
// Returns the navigation decorator rectangle used in the narrow layout.
// ---------------------------------------------------------------------------
//
TRect CAknNavigationDecorator::DecoratedControlNarrowRect(
    TInt /*aControlType*/ )
    {
    TBool landscape( Layout_Meta_Data::IsLandscapeOrientation() );

    // Status pane area
    TRect statusPaneRect;
    AknLayoutUtils::LayoutMetricsRect(
        AknLayoutUtils::EStatusPane, statusPaneRect );
        
    // Navi pane area
    TRect naviPaneRect;
    AknLayoutUtils::LayoutMetricsRect(
        AknLayoutUtils::ENaviPane, naviPaneRect );

    // Short navi pane
    TAknLayoutRect naviSrtRect;        
    naviSrtRect.LayoutRect(
        statusPaneRect,
        AknLayoutScalable_Avkon::navi_pane_srt( 0 ) );        

    TRect rect( naviSrtRect.Rect() );
        
    // Set relative to navipane coordinates
    rect.Move( -naviPaneRect.iTl.iX, 0 );
    rect.iTl.iY = 0;
    rect.SetHeight( naviSrtRect.Rect().Height() );

    return rect;
    }


// ---------------------------------------------------------------------------
// CAknNavigationDecorator::DecoratedControlRect
// Returns the rectangle of the navigation decorator in relation to the
// navi pane.
// ---------------------------------------------------------------------------
//
TRect CAknNavigationDecorator::DecoratedControlRect( TInt aControlType,
                                                     TBool aArrowsUsed )
    {
    TRect rect;

    switch ( aControlType )
        {
        case ETabGroup:
            {
            rect = DecoratedTabControlRect( ETrue, aArrowsUsed );
            break;
            }
        case ENaviVolume:
            {
            rect = DecoratedVolumeControlRect();
            break;
            }
        default:
            {
            rect = DecoratedDefaultControlRect();
            break;
            }
        }

    return rect;
    }


// ---------------------------------------------------------------------------
// CAknNavigationDecorator::DecoratedControlRect
// Returns the default rectangle of the navigation decorator.
// ---------------------------------------------------------------------------
//
TRect CAknNavigationDecorator::DecoratedDefaultControlRect()
    {
    TRect rect;
    
    TRect naviPaneRect;
    AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ENaviPane, naviPaneRect );

    if ( AknStatuspaneUtils::SmallLayoutActive() )
        {
        // screen
        TRect screenRect;
        AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EScreen, screenRect );
    
        // top area
        TAknLayoutRect topAreaLayoutRect;        
        topAreaLayoutRect.LayoutRect(
            screenRect,
            AknLayoutScalable_Avkon::area_top_pane( 1 ) );
        
        // small statuspane      
        TAknLayoutRect statusPaneLayoutRect;
        statusPaneLayoutRect.LayoutRect(
            topAreaLayoutRect.Rect(),
            AknLayoutScalable_Avkon::status_small_pane() );
        TRect statusPaneRect( statusPaneLayoutRect.Rect() );
        
        TAknWindowComponentLayout signalPaneLayout(
            AknLayoutScalable_Avkon::status_small_pane_g2( 0 ) );
        TAknLayoutRect signalPaneLayoutRect;
        signalPaneLayoutRect.LayoutRect( statusPaneRect, signalPaneLayout );

        rect = statusPaneRect; 

        CEikStatusPaneBase* statusPane = CEikStatusPaneBase::Current();
        if ( statusPane &&
             statusPane->PaneCapabilities(TUid::Uid(EEikStatusPaneUidSignal)).IsInCurrentLayout() )
            {
            TSize size( rect.Size() );
            size.iWidth -= signalPaneLayoutRect.Rect().Width();            
            rect.SetSize( size );
            }
        
        // Adjust the size for the navi pane content so that there's space for
        // the possible navi arrows.
        TInt arrowWidth = NaviArrowRect( ELeftButton ).Width();
        rect.iTl.iX += arrowWidth;
        rect.iBr.iX -= arrowWidth;
        }
    else if ( AknStatuspaneUtils::StaconPaneActive() )
        {
        // navi navi pane
        TAknLayoutRect naviNaviLayoutRect;        
        naviNaviLayoutRect.LayoutRect(
            naviPaneRect,
            AknLayoutScalable_Avkon::navi_navi_pane() );        
        
        // pane for text and graphics
        TAknLayoutRect naviControlLayoutRect;        
        naviControlLayoutRect.LayoutRect(
            naviNaviLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_icon_text_pane() );
        
        rect = naviControlLayoutRect.Rect();
        
        // Set relative to stacon navipane coordinates
        rect.Move( -naviPaneRect.iTl.iX,
                   -naviControlLayoutRect.Rect().iTl.iY ); 
        }       
    else
        {
        // navi navi pane
        TAknLayoutRect naviNaviLayoutRect;        
        naviNaviLayoutRect.LayoutRect(
            naviPaneRect,
            AknLayoutScalable_Avkon::navi_navi_pane() );        
        
        // pane for text and graphics
        TAknLayoutRect naviControlLayoutRect;        
        naviControlLayoutRect.LayoutRect(
            naviNaviLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_icon_text_pane() );         
        TRect naviControlRect( naviControlLayoutRect.Rect() );
        
        rect = naviControlRect;
        rect.Move( -naviNaviLayoutRect.Rect().iTl.iX,
                   -naviControlRect.iTl.iY );
        }

    return rect;
    }


// ---------------------------------------------------------------------------
// CAknNavigationDecorator::DecoratedTabControlRect
// Returns the tab group rectangle in relation to the navi pane.
// ---------------------------------------------------------------------------
//
TRect CAknNavigationDecorator::DecoratedTabControlRect( TBool aTopAdjacent,
                                                        TBool aArrowsUsed )
    {
    TRect rect;

    // "Stacon" statuspane
    if ( AknStatuspaneUtils::StaconPaneActive() )
        {
        TRect naviPaneRect;
        AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ENaviPane,
                                           naviPaneRect );

        TInt variety = AknStatuspaneUtils::ExtendedStaconPaneActive() ? 1 : 0;

        TAknLayoutRect staconNaviTabRect;
        staconNaviTabRect.LayoutRect(
            naviPaneRect,
            AknLayoutScalable_Avkon::navi_navi_pane_stacon( variety ) );

        // tab pane
        TAknLayoutRect tabsPaneLayoutRect;
        tabsPaneLayoutRect.LayoutRect(
            staconNaviTabRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_tabs_pane() );
        TRect tabsRect( tabsPaneLayoutRect.Rect() );

        rect = tabsRect;

        // Set relative to stacon navipane coordinates
        rect.Move( -staconNaviTabRect.Rect().iTl.iX, -tabsRect.iTl.iY );
        }
    else
        {
        TBool penEnabled( AknLayoutUtils::PenEnabled() );
        TRect naviPaneRect;
        AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ENaviPane,
                                           naviPaneRect );

        if ( !penEnabled )
            {
            // Tab pane is the only control which doesn't use the wide navi pane
            // layout in non-touch portrait mode, so we can't get the navi
            // pane rect directly from AknLayoutUtils.
            TRect statusPaneRect;
            AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EStatusPane,
                                               statusPaneRect );

            TAknLayoutRect naviPaneLayoutRect;
            naviPaneLayoutRect.LayoutRect(
                statusPaneRect,
                AknLayoutScalable_Avkon::navi_pane( 5 ) );
            naviPaneRect = naviPaneLayoutRect.Rect();
            }
        
        if ( aArrowsUsed )
            {
            // navi navi pane
            TAknLayoutRect naviNaviPaneLayoutRect;
            naviNaviPaneLayoutRect.LayoutRect(
                naviPaneRect,
                AknLayoutScalable_Avkon::navi_navi_pane() );
    
            // tab pane
            TAknLayoutRect tabsPaneLayoutRect;
            tabsPaneLayoutRect.LayoutRect(
                naviNaviPaneLayoutRect.Rect(),
                AknLayoutScalable_Avkon::navi_navi_tabs_pane() );
            TRect tabsRect( tabsPaneLayoutRect.Rect() );
            
            rect = tabsRect;
            
            if ( aTopAdjacent )
                {
                rect.Move( -naviNaviPaneLayoutRect.Rect().iTl.iX,
                           -tabsRect.iTl.iY );
                }
            else
                {
                rect.Move( -naviNaviPaneLayoutRect.Rect().iTl.iX,
                           -( tabsRect.iTl.iY - ( naviPaneRect.Height() -
                                                  tabsRect.Height() ) ) );
                }
            }
        else
            {
            rect = naviPaneRect;
            rect.Move( -naviPaneRect.iTl.iX,
                       -naviPaneRect.iTl.iY );
            }
        }

    return rect;
    }


TRect CAknNavigationDecorator::DecoratedVolumeControlRect()
    {
    TRect rect;
    
    // screen
    TRect screenRect;
    AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EScreen, screenRect );
    
    if ( AknStatuspaneUtils::SmallLayoutActive() )
        {
        // top area
        TAknLayoutRect topAreaLayoutRect;        
        topAreaLayoutRect.LayoutRect(
            screenRect,
            AknLayoutScalable_Avkon::area_top_pane( 1 ) );
        
        // small statuspane      
        TAknLayoutRect statusPaneLayoutRect;
        statusPaneLayoutRect.LayoutRect(
            topAreaLayoutRect.Rect(),
            AknLayoutScalable_Avkon::status_small_pane() );
        TRect statusPaneRect( statusPaneLayoutRect.Rect() );
        
        rect = statusPaneRect; 
        
        statusPaneLayoutRect.LayoutRect(
            rect,
            AknLayoutScalable_Avkon::status_small_volume_pane() );
        rect = statusPaneLayoutRect.Rect();
        }
        
    else if ( AknStatuspaneUtils::StaconPaneActive() )
        {
        // bottom area
        TAknLayoutRect bottomAreaLayoutRect;        
        bottomAreaLayoutRect.LayoutRect(
            screenRect,
            AknLayoutScalable_Avkon::area_bottom_pane( 2 ) );        
            
        // stacon bottom
        TAknLayoutRect staconBottomLayoutRect;
        staconBottomLayoutRect.LayoutRect(
            bottomAreaLayoutRect.Rect(),
            AknLayoutScalable_Avkon::stacon_bottom_pane() );
            
        TInt naviPaneVariety = AknStatuspaneUtils::StaconSoftKeysRight() ? 2 : 3;
        TAknLayoutRect staconNaviRect;        
        staconNaviRect.LayoutRect(
            staconBottomLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_pane_stacon( naviPaneVariety ) );        

        TAknLayoutRect staconNaviControlRect;        
        staconNaviControlRect.LayoutRect(
            staconNaviRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_pane() );
        
        TAknLayoutRect volumeControlRect;
        volumeControlRect.LayoutRect(
            staconNaviControlRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_volume_pane() );

        rect = volumeControlRect.Rect();
        
        // Set relative to stacon navipane coordinates
        rect.Move( -staconNaviRect.Rect().iTl.iX, 0 ); 
        rect.iTl.iY = 0;
        rect.SetHeight( volumeControlRect.Rect().Height() );
        }
        
    // "Flat" statuspane
    else if ( AknStatuspaneUtils::FlatLayoutActive() )
        {
        TAknLayoutRect naviRect;        
        naviRect.LayoutRect(
            screenRect,
            AknLayoutScalable_Avkon::navi_pane( 2 ) );
        
        TAknLayoutRect naviNaviLayoutRect;        
        naviNaviLayoutRect.LayoutRect(
            naviRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_pane() );
        
        TAknLayoutRect volumeControlRect;
        volumeControlRect.LayoutRect(
            naviNaviLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_volume_pane() );
            
        rect = volumeControlRect.Rect();
        
        rect.Move( -naviNaviLayoutRect.Rect().iTl.iX, 0 ); 
        rect.iTl.iY = 0;
        rect.SetHeight( volumeControlRect.Rect().Height() );
        }
        
    else
        {        
        // Is battery pane visible in current layout
        TInt battery = AknStatuspaneUtils::ExtendedLayoutActive() ? 5 : 0; // classic or extended
        
        if ( AknStatuspaneUtils::IdleLayoutActive() )
            {
            battery = AknStatuspaneUtils::ExtendedLayoutActive() ? 6 : 1; // classic or extended
            }
        else if ( AknStatuspaneUtils::UsualLayoutActive() &&
                  !Layout_Meta_Data::IsLandscapeOrientation() )
            {
            battery = 7;
            }

        if ( AknStatuspaneUtils::HDLayoutActive() && battery == 5 )
            {
            battery = 8;
            }

        // app window
        TAknLayoutRect applicationWindowLayoutRect;
        applicationWindowLayoutRect.LayoutRect(
            screenRect,
            AknLayoutScalable_Avkon::application_window( 0 ) );
        
        // statuspane
        TAknLayoutRect statusPaneLayoutRect;
        statusPaneLayoutRect.LayoutRect(
            applicationWindowLayoutRect.Rect(),
            AknLayoutScalable_Avkon::status_pane( 0 ) );
        
        // navi pane
        TAknLayoutRect naviPaneLayoutRect;
        naviPaneLayoutRect.LayoutRect(
            statusPaneLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_pane( battery ) );
        
        TAknLayoutRect naviNaviLayoutRect;        
        naviNaviLayoutRect.LayoutRect(
            naviPaneLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_pane() );
        
        TAknLayoutRect volumeControlRect;
        volumeControlRect.LayoutRect(
            naviNaviLayoutRect.Rect(),
            AknLayoutScalable_Avkon::navi_navi_volume_pane() );
        TRect volumeRect( volumeControlRect.Rect() );
        
        rect = volumeRect;
        
        rect.Move( -naviNaviLayoutRect.Rect().iTl.iX, -volumeRect.iTl.iY );
        }

    return rect;
    }

EXPORT_C void CAknNavigationDecorator::SetNaviControlLayoutStyle(
    TAknNaviControlLayoutStyle aStyle )
    {
    if ( AknStatuspaneUtils::FlatLayoutActive() &&
         !Layout_Meta_Data::IsLandscapeOrientation() )
        {
        // In portrait flat sp mode only narrow mode is allowed.
        iLayoutFlags &= ~ENaviControlLayoutNarrow;
        iLayoutFlags &= ~ENaviControlLayoutNormal;
        iLayoutFlags |= ENaviControlLayoutNarrow;
        }
    else if ( NaviControlLayoutStyleSupported( aStyle ) )
        {
        iLayoutFlags &= ~ENaviControlLayoutNarrow;
        iLayoutFlags &= ~ENaviControlLayoutNormal;
        iLayoutFlags &= ~ENaviControlLayoutWide;
        iLayoutFlags |= aStyle;        
        }
        
    if ( NaviControlLayoutMode() == ENaviControlLayoutModeAutomatic &&
         NaviControlLayoutStyle() == ENaviControlLayoutNormal )
        {
        InitLayoutStyleTimer();
        }
    else
        {
        CancelLayoutStyleTimer();
        }

    if ( iContainer )
        {
        iContainer->SetSize( iContainer->Size() );
        }
    else
        {
        SizeChanged();
        }
    }


EXPORT_C CAknNavigationDecorator::TAknNaviControlLayoutStyle CAknNavigationDecorator::NaviControlLayoutStyle()
    {
    if ( iLayoutFlags & ENaviControlLayoutNarrow )
        {
        return ENaviControlLayoutNarrow;
        }
    else if ( iLayoutFlags & ENaviControlLayoutWide )
        {
        return ENaviControlLayoutWide;
        }
    else
        {
        return ENaviControlLayoutNormal;
        }
    }


EXPORT_C void CAknNavigationDecorator::SetNaviControlLayoutMode(
    TAknNaviControlLayoutMode aMode )
    {
    iLayoutFlags &= ~ENaviControlLayoutModeAutomatic;                
    iLayoutFlags &= ~ENaviControlLayoutModeForced;                
    iLayoutFlags |= aMode;

    if ( NaviControlLayoutMode() == ENaviControlLayoutModeForced )
        {
        CancelLayoutStyleTimer();
        }
    else 
        {
        if ( NaviControlLayoutMode()  == ENaviControlLayoutModeAutomatic &&
             NaviControlLayoutStyle() == ENaviControlLayoutNormal )
            {
            InitLayoutStyleTimer();
            }        
        }          
    }


EXPORT_C CAknNavigationDecorator::TAknNaviControlLayoutMode CAknNavigationDecorator::NaviControlLayoutMode()
    {
    if ( iLayoutFlags & ENaviControlLayoutModeForced )
        {
        return ENaviControlLayoutModeForced;
        }
    else
        {
        return ENaviControlLayoutModeAutomatic;
        }
    }


EXPORT_C TBool CAknNavigationDecorator::NaviControlLayoutStyleSupported(
    TAknNaviControlLayoutStyle aStyle )
    {
    TBool retVal( EFalse );
    TBool penEnabled( AknLayoutUtils::PenEnabled() );

    if ( aStyle == ENaviControlLayoutNormal )
        {
        retVal = ETrue;
        }
    else if ( aStyle == ENaviControlLayoutNarrow )
        {
        if ( ( iControlType == ETabGroup && !penEnabled ) ||
             iControlType == ENaviLabel ||
             iControlType == ENaviImage ||
             iControlType == EHintText ||
             iControlType == EEditorIndicator )
            {
            retVal = ETrue;
            }
        }
    else if ( aStyle == ENaviControlLayoutWide )
        {
        // Wide layout is not currently supported for tabs in non-touch
        // layouts.
        if ( iControlType == ETabGroup && !penEnabled )
            {
            retVal = EFalse;
            }
        else
            {
            retVal = ETrue;
            }
        }

    return retVal;
    }


void CAknNavigationDecorator::InitLayoutStyleTimer()
    {
    if ( IsVisible() )
        {
        const TInt KIdleDelay = 5000000; // 5 s
        iLayoutStyleTimer->Cancel();    
        iLayoutStyleTimer->Start(TTimeIntervalMicroSeconds32(KIdleDelay),
            TTimeIntervalMicroSeconds32(KIdleDelay), 
            TCallBack(CAknNavigationDecorator::LayoutStyleEvent, this));
        }
    }

void CAknNavigationDecorator::CancelLayoutStyleTimer()
    {
    if (iLayoutStyleTimer && iLayoutStyleTimer->IsActive())
        {
        iLayoutStyleTimer->Cancel();
        }    
    }

TInt CAknNavigationDecorator::LayoutStyleEvent( TAny* aPtr )
    {
    ( ( CAknNavigationDecorator*) aPtr )->DoLayoutStyleEvent();
    return ETrue;
    }

void CAknNavigationDecorator::DoLayoutStyleEvent()
    {
    // First check that status pane layout or layout mode
    // has not been changed during timeout time.
    if ( !iContainer ||
         ( iContainer && iContainer->Top() != this ) ||
         !AknStatuspaneUtils::FlatLayoutActive() ||
         NaviControlLayoutMode() != ENaviControlLayoutModeAutomatic ||
         !IsVisible() )
        {
        CancelLayoutStyleTimer();
        return; 
        }
    
    // Only if automatic mode is active and control supports narrow layout style 
    // and titlepane text does not fit completely in normal mode
    // we change the layout style...
    CEikStatusPaneBase* statusPane = CEikStatusPaneBase::Current();
    CCoeControl* navipanecontrol = NULL;
    CCoeControl* titlepaneControl = NULL;
    TBool textDoesFitVisibleTitlePaneArea = EFalse;
    
    if ( statusPane )
        {
        TRAP_IGNORE( navipanecontrol =
            statusPane->ControlL(TUid::Uid( EEikStatusPaneUidNavi ) ) );
        CAknTitlePane* titlePane =
            dynamic_cast <CAknTitlePane*> ( titlepaneControl );
            
        TRAP_IGNORE( titlepaneControl =
            statusPane->ControlL(TUid::Uid( EEikStatusPaneUidTitle ) ) );
    
        if ( navipanecontrol && titlePane )
            {        
            CAknTitlePaneLabel* titleLabel =
                static_cast <CAknTitlePaneLabel*> ( titlePane->TextLabel() );
                
            if ( titleLabel )
                {
                // check it text fits, add some safety margin (15%)
                TInt textlength = titleLabel->TextLength();
                TInt visibleAreaForText =
                    titlepaneControl->Size().iWidth - navipanecontrol->Size().iWidth;
                if ( textlength < ( visibleAreaForText * 85 / 100) )
                    {
                    textDoesFitVisibleTitlePaneArea = ETrue;
                    }
                }            
            }
        }
    
    if ( NaviControlLayoutMode() == ENaviControlLayoutModeAutomatic &&
         NaviControlLayoutStyleSupported( ENaviControlLayoutNarrow ) &&
         !textDoesFitVisibleTitlePaneArea )
        {
        SetNaviControlLayoutStyle( ENaviControlLayoutNarrow );         
        SizeChanged();
        DrawDeferred();
        }    
    
    CancelLayoutStyleTimer();
    }

TInt CAknNavigationDecorator::IndicationDrawCallbackL( TAny* aThis )
    {
    CAknNavigationDecorator* decorator = 
        static_cast<CAknNavigationDecorator*>(aThis);
    
    decorator->SmallDirectionIndicationL();
    
    return KErrNone;
    }

void CAknNavigationDecorator::SmallDirectionIndicationL()
    {
    TRect rect;
    
    // Cancel blinking if moved from the min or max position,
    // unless the area is blank. Then redraw and cancel after redraw
    if ( !iNaviArrowLeftDimmed &&
         !iNaviArrowRightDimmed && 
         ( iExtension->iEffectTimerCount % 2 == 0 ) )
        {
        CancelTimer();
        return;
        }
    
    // Stop timer if done normal-inverted-normal-inverted-normal sequence
    // or the user has changed the value from the min or max
    if ( iExtension->iEffectTimerCount >= 3 || 
         ( !iNaviArrowLeftDimmed &&
           !iNaviArrowRightDimmed ) )
        {
        CancelTimer();
        }
    
    Window().Invalidate( rect );
    ActivateGc();
            
    rect = TRect( iArrowLeftSize );
    
    // Draw navigation pane scroll indicator arrows if visible
    if ( iNaviArrowsVisible && iContainer )
        {
        CWindowGc& gc = SystemGc();
        MAknsSkinInstance* skin = AknsUtils::SkinInstance();
        MAknsControlContext* cc = AknsDrawUtils::ControlContext( this );
        
        CFbsBitmap* colorbmp = iContainer->NaviColorBitmap();

        if ( iExtension->iBlinkingSide == 
             CAknNavigationDecoratorExtension::ENaviDecoratorLeftSide ) 
            {
            if( iExtension->iEffectTimerCount % 2 == 1 &&
                !iNaviArrowLeftDimmed )
                {
                CFbsBitmap* arrowBmp = &(iContainer->NaviArrowBitmap(0));
                CFbsBitmap* arrowMask = &(iContainer->NaviArrowBitmap(1));
                if( colorbmp && !IsMaskAllBlack( arrowMask ) )
                    {
                    gc.BitBltMasked( iArrowLeftPos,
                                     colorbmp,
                                     rect, 
                                     arrowMask,
                                     ETrue );
                    }
                else
                    {
                    gc.BitBltMasked( iArrowLeftPos,
                                     arrowBmp,
                                     rect,
                                     arrowMask,
                                     ETrue );
                    }
                }
            else
                {
                // draw empty
                AknsDrawUtils::Background(
                    skin,
                    cc,
                    this,
                    gc, 
                    TRect( iArrowLeftPos, rect.Size() ),
                    KAknsDrawParamDefault );
                }
            }
        rect = TRect( iArrowRightSize );
        
        if ( iExtension->iBlinkingSide == 
             CAknNavigationDecoratorExtension::ENaviDecoratorRightSide ) 
            {
            if ( iExtension->iEffectTimerCount % 2 == 1 &&
                 !iNaviArrowRightDimmed )
                {
                CFbsBitmap* arrowBmp = &(iContainer->NaviArrowBitmap(2));
                CFbsBitmap* arrowMask = &(iContainer->NaviArrowBitmap(3));
                if( colorbmp && !IsMaskAllBlack( arrowMask ) )
                    {
                    gc.BitBltMasked( iArrowRightPos,
                                     colorbmp,
                                     rect,
                                     arrowMask,
                                     ETrue );
                    }
                else
                    {
                    gc.BitBltMasked( iArrowRightPos,
                                     arrowBmp,
                                     rect,
                                     arrowMask,
                                     ETrue );
                    }
                }
            else
                {
                // draw empty
                AknsDrawUtils::Background(
                    skin,
                    cc,
                    this,
                    gc, 
                    TRect( iArrowRightPos, rect.Size() ),
                    KAknsDrawParamDefault );
                }
            }
        }

    DeactivateGc();
    
    iExtension->iEffectTimerCount++;
    }


void CAknNavigationDecorator::StartTimerL()
    {
    if ( !iExtension->iTimer )
        {
        iExtension->iTimer = CPeriodic::NewL( CActive::EPriorityStandard );
        }
    else if ( iExtension->iTimer->IsActive() )
        {
        return; // do not re-start as we have the feedback ongoing
        }
    
    iExtension->iEffectTimerCount = 0;

    const TTimeIntervalMicroSeconds32 KArrowFeedbackActionTime = 160 * 1000;
    
    TCallBack callback( IndicationDrawCallbackL, this );
    iExtension->iTimer->Start( KArrowFeedbackActionTime,
                               KArrowFeedbackActionTime,
                               callback );
    }
    
void CAknNavigationDecorator::CancelTimer()
    {
    if ( iExtension && iExtension->iTimer )
        {
        if ( iExtension->iTimer->IsActive() )
            {
            iExtension->iTimer->Cancel();
            }
        }
    }

EXPORT_C void CAknNavigationDecorator::HandleResourceChange( TInt aType )
    {
    if ( iControlType == ENaviVolume )
        {
        // We must forward the event to the volume popup instead
        // of the old style volume control.
        iDecoratedControl->HandleResourceChange( aType );
        }
    else
        {
        CCoeControl::HandleResourceChange( aType );
        }

    if ( aType == KEikDynamicLayoutVariantSwitch )
        {
        TAknNaviControlLayoutStyle layoutStyle = NaviControlLayoutStyle();
        TAknNaviControlLayoutMode  layoutMode  = NaviControlLayoutMode();
        TBool sizeChangedCalled( EFalse );
        
        if ( layoutMode  == ENaviControlLayoutModeAutomatic &&
             layoutStyle == ENaviControlLayoutNarrow )
            {
            SetNaviControlLayoutStyle( ENaviControlLayoutNormal );
            sizeChangedCalled = ETrue;
            }
        else if ( iControlType == ETabGroup &&
                  AknStatuspaneUtils::UsualLayoutActive() &&
                  !Layout_Meta_Data::IsLandscapeOrientation() )
            {
            // Need special handling for tab group in portrait mode usual
            // layout if switching from touch layout to a non-touch layout
            // or vice versa since the wide layout is not supported in
            // non-touch layout but is always used for tab groups in
            // portrait touch layouts.
            if ( AknLayoutUtils::PenEnabled() )
                {
                if ( layoutStyle != ENaviControlLayoutWide )
                    {
                    SetNaviControlLayoutMode( ENaviControlLayoutModeForced );
                    SetNaviControlLayoutStyle( ENaviControlLayoutWide );
                    sizeChangedCalled = ETrue;
                    }
                }
            else
                {
                if ( layoutStyle == ENaviControlLayoutWide )
                    {
                    SetNaviControlLayoutMode( ENaviControlLayoutModeAutomatic );
                    SetNaviControlLayoutStyle( ENaviControlLayoutNormal );
                    sizeChangedCalled = ETrue;
                    }
                }
            }

        if ( !sizeChangedCalled )
            {
            SizeChanged();
            }
        }
    }


TRect CAknNavigationDecorator::NaviArrowRect( TScrollButton aScrollButton,
                                              TBool aNarrowLayout,
                                              TRect aNaviRect )
    {
    TAknLayoutRect layoutRect;
    TRect arrowRect;

    TAknWindowComponentLayout arrowLayout;
    if ( aNarrowLayout )
        {
        arrowLayout = aScrollButton == ELeftButton ?
            AknLayoutScalable_Avkon::navi_navi_pane_srt_g1( 0 ) :
            AknLayoutScalable_Avkon::navi_navi_pane_srt_g2( 0 );
        }
    else
        {
        arrowLayout = aScrollButton == ELeftButton ?
            AknLayoutScalable_Avkon::navi_navi_pane_g1( 0 ) :
            AknLayoutScalable_Avkon::navi_navi_pane_g2( 0 );
        }

    // aNaviRect is empty by default
    if ( !aNaviRect.IsEmpty() )
        {
        layoutRect.LayoutRect( aNaviRect, arrowLayout );
        arrowRect = layoutRect.Rect();
        }
    else
        {
        if ( aNarrowLayout )
            {
            TRect naviPaneRect( DecoratedControlNarrowRect( 0 ) ); // parameter not used
            layoutRect.LayoutRect( naviPaneRect, arrowLayout );
            arrowRect = layoutRect.Rect();
            }
        else
            {
            TRect naviPaneRect;
            AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ENaviPane,
                                               naviPaneRect );

            naviPaneRect.Move( -naviPaneRect.iTl.iX,
                               -naviPaneRect.iTl.iY );

            layoutRect.LayoutRect(
                naviPaneRect,
                AknLayoutScalable_Avkon::navi_navi_pane() );
            TRect naviNaviRect( layoutRect.Rect() );

            layoutRect.LayoutRect( naviNaviRect, arrowLayout );
            arrowRect = layoutRect.Rect();
            }
        }

    return arrowRect;
    }

//  End of File  
