/*
* Copyright (c) 2007-2010 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:  Content that is shown inside a universal status
*                indicator popup.
*
*/


// SYSTEM INCLUDE FILES

#include <AknsDrawUtils.h>
#include <aknlayoutscalable_avkon.cdl.h>
#include <AknsConstants.h>
#include <AknUtils.h>
#include "akndigitalclock.h"
#include <avkon.mbg>
#include <gulicon.h>
#include <barsread.h>
#include <AknIndicator.h>
#include <AknIndicatorPlugin.h>
#include <AknIndicatorPluginImplUIDs.hrh>
#include <AknIndicatorContainer.h>
#include <apgcli.h>
#include <aknconsts.h>
#include <aknglobalpopupprioritycontroller.h>
#include <AknTasHook.h> // for testability hooks
#include <touchfeedback.h>


// USER INCLUDE FILES
#include "aknindicatorpopupcontent.h"
#include "aknindicatorpopup.h"
#include "AknBatteryIcon.h"
#include "AknSignalIcon.h"


// CONSTANTS

/** Granularity of the indicator item array. */
const TInt KItemArrayGranularity = 8;

/** Path to the clock application, launched directly from the popup. */
_LIT( KClockApplication, "\\sys\\bin\\clock.exe" );

/** Internal state flags. */
enum TIndicatorPopupContentFlags
    {
    /** Pointer down event has happened inside this control. */
    EAknIndicatorPopupContentButton1Down        = 0x00000001,
    /** Clock application is being launched. */
    EAknIndicatorPopupContentClockBeingLaunched = 0x00000002
    };

const TInt KBatteryAreaIndex = 0;
const TInt KSignalAreaIndex  = 1;


// =============================================================================
// CAknIndicatorPopupItem
// Item class declaration & definition.
// =============================================================================
//
NONSHARABLE_CLASS( CAknIndicatorPopupItem ) : public CBase
    {
public:

    /**
    * Default constructor.
    *
    * @param  aUid       UID of the indicator.
    * @param  aPlugin    Pointer to the indicator's ECOM plugin,
    *                    can be NULL.
    * @param  aPriority  Display priority of the indicator.
    * @param  aTextType  Type of the item text.
    */
    CAknIndicatorPopupItem( TInt aUid,
                            CAknIndicatorPlugin* aPlugin,
                            TInt aPriority,
                            TInt aTextType );

    /**
    * Destructor.
    */
    ~CAknIndicatorPopupItem();

    /**
    * Second-phase constructor to be used with custom icon.
    *
    * @param  aText  Item text.
    * @param  aIcon  Indicator icon.
    */
    void ConstructL( const TDesC& aText,
                     const CGulIcon* aIcon );

    /**
    * Second-phase constructor to be used with the default
    * indicator icon.
    *
    * @param  aText    Item text.
    * @param  aIconID  Index of the indicator icon.
    * @param  aIconID  Index of the indicator icon mask.
    */
    void ConstructL( const TDesC& aText,
                     TInt aIconID,
                     TInt aIconMaskID );

    /**
    * Sets the area of this item in the popup.
    *
    * @param  aRect  Rectangle of this item, relative
    *                to the popup.
    */
    void SetRect( const TRect& aRect );

    /**
    * Changes the indicator icon of the item or updates
    * the color when skin changes.
    *
    * @param  aIcon  Pointer to the new icon if the icon
    *                is to be changed.
    */
    void UpdateIndicatorIconL( const CGulIcon* aIcon = NULL );

    /**
    * Updates the item text.
    *
    * @param  aText  Item text.
    */
    void UpdateTextL( const TDesC& aText, TInt aTextType );

    /**
    * Draws the item.
    *
    * @param  aGc  Reference to the window graphics context.
    */
    void DrawItem( CWindowGc& aGc );

public: // Member data

    /** UID of the indicator. */
    TInt                  iUid;

    /** Indicator priority. */
    TInt                  iPriority;

    /** Item text, owned. */
    HBufC*                iText;

    /** Type of the text. */
    TInt                  iTextType;

    /** Item area. */
    TRect                 iRect;

    /** Indicator icon area. */
    TRect                 iIconRect;

    /** Default indicator icon, owned. */
    CGulIcon*             iIcon;

    /** Custom indicator icon, not owned. */
    const CGulIcon*       iCustomIcon;

    /** Index of the default indicator icon. */
    TInt                  iIconID;

    /** Index of the default indicator icon mask. */
    TInt                  iIconMaskID;

    /** Pointer to the indicator plugin, not owned. */
    CAknIndicatorPlugin*  iPlugin;
    };


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::CAknIndicatorPopupItem
// Default constructor
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupItem::CAknIndicatorPopupItem( TInt aUid,
                                                CAknIndicatorPlugin* aPlugin,
                                                TInt aPriority,
                                                TInt aTextType )
    : iUid( aUid ),
      iPriority( aPriority ),
      iTextType( aTextType ),
      iPlugin( aPlugin )
    {
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::CAknIndicatorPopupItem
// Destructor
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupItem::~CAknIndicatorPopupItem()
    {
    delete iText;
    delete iIcon;
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::ConstructL
// Second-phase constructor
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupItem::ConstructL( const TDesC& aText,
                                         const CGulIcon* aIcon )
    {
    delete iText;
    iText = NULL;
    iText = aText.AllocL();

    iCustomIcon = aIcon;
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::ConstructL
// Second-phase constructor
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupItem::ConstructL( const TDesC& aText,
                                         TInt aIconID,
                                         TInt aIconMaskID )
    {
    delete iText;
    iText = NULL;
    iText = aText.AllocL();

    iIconID     = aIconID;
    iIconMaskID = aIconMaskID;

    UpdateIndicatorIconL();
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::SetRect
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupItem::SetRect( const TRect& aRect )
    {
    iRect = aRect;

    TAknLayoutRect layoutRect;
    layoutRect.LayoutRect(
        iRect,
        AknLayoutScalable_Avkon::list_single_uniindi_pane_g1() );
    iIconRect = layoutRect.Rect();

    CFbsBitmap* bitmap = NULL;

    if ( iCustomIcon )
        {
        bitmap = iCustomIcon->Bitmap();
        }
    else if ( iIcon )
        {
        bitmap = iIcon->Bitmap();
        }

    if ( bitmap )
        {
        AknIconUtils::SetSize( bitmap,
                               iIconRect.Size(),
                               EAspectRatioPreserved );
        }
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::UpdateIndicatorIconL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupItem::UpdateIndicatorIconL( const CGulIcon* aIcon )
    {
    if ( iCustomIcon &&
         !aIcon )
        {
        // Custom icon in use
        if ( iPlugin )
            {
            iCustomIcon = NULL;
            TRAPD( err, iCustomIcon = iPlugin->IconL( iUid ) );

            if ( !err && iCustomIcon )
                {
                // Icon update succeeded.
                AknIconUtils::SetSize( iCustomIcon->Bitmap(),
                                       iIconRect.Size(),
                                       EAspectRatioPreserved );
                return;
                }

            // Else use the default icon.
            }
        }

    delete iIcon;
    iIcon = NULL;

    if ( aIcon )
        {
        iCustomIcon = aIcon;

        AknIconUtils::SetSize( iCustomIcon->Bitmap(),
                               iIconRect.Size(),
                               EAspectRatioPreserved );
        }
    else
        {
        CFbsBitmap* bitmap;
        CFbsBitmap* mask;

        MAknsSkinInstance* skin = AknsUtils::SkinInstance();

        AknsUtils::CreateColorIconLC(
            skin,
            KAknsIIDDefault,
            KAknsIIDQsnIconColors,
            EAknsCIQsnIconColorsCG20,
            bitmap,
            mask,
            KAvkonBitmapFile,
            iIconID,
            iIconMaskID,
            KRgbGray );

        AknIconUtils::SetSize( bitmap,
                               iIconRect.Size(),
                               EAspectRatioPreserved );

        iIcon = CGulIcon::NewL( bitmap, mask );

        CleanupStack::Pop( 2, bitmap );
        }
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::UpdateTextL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupItem::UpdateTextL( const TDesC& aText, TInt aTextType )
    {
    delete iText;
    iText = NULL;
    iText = aText.AllocL();
    iTextType = aTextType;
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupItem::DrawItem
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupItem::DrawItem( CWindowGc& aGc )
    {
    // Use custom icon if it exists.
    const CGulIcon* icon = iCustomIcon;
    if ( !icon )
        {
        icon = iIcon;
        }

    if ( icon && icon->Bitmap() && icon->Mask() )
        {
        aGc.BitBltMasked( iIconRect.iTl,
                          icon->Bitmap(),
                          iIconRect.Size(),
                          icon->Mask(),
                          ETrue );
        }

    MAknsSkinInstance* skin = AknsUtils::SkinInstance();

    TAknTextLineLayout textLayout(
        AknLayoutScalable_Avkon::list_single_uniindi_pane_t1().LayoutLine() );

    TRgb textColor;

    if ( iTextType == CAknIndicatorPlugin::EAknIndicatorPluginLinkText )
        {
        aGc.SetUnderlineStyle( EUnderlineOn );
        AknsUtils::GetCachedColor( skin,
                                   textColor,
                                   KAknsIIDQsnHighlightColors,
                                   EAknsCIQsnHighlightColorsCG3 );
        }
    else
        {
        AknsUtils::GetCachedColor( skin,
                                   textColor,
                                   KAknsIIDQsnTextColors,
                                   EAknsCIQsnTextColorsCG55 );
        }

    TAknLayoutText layoutText;
    layoutText.LayoutText( iRect, textLayout );
    layoutText.DrawText( aGc, *iText, ETrue, textColor );

    aGc.SetUnderlineStyle( EUnderlineOff );
    }


// =============================================================================
// CAknIndicatorPopupContent
// =============================================================================
//

// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::NewL
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupContent* CAknIndicatorPopupContent::NewL()
    {
    CAknIndicatorPopupContent* self = CAknIndicatorPopupContent::NewLC();
    CleanupStack::Pop( self );
    return self;
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::NewLC
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupContent* CAknIndicatorPopupContent::NewLC()
    {
    CAknIndicatorPopupContent* self = new (ELeave) CAknIndicatorPopupContent;
    CleanupStack::PushL( self );
    self->ConstructL();
    AKNTASHOOK_ADDL( self, "CAknIndicatorPopupContent" );
    return self;
    }


// -----------------------------------------------------------------------------
// Destructor.
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupContent::~CAknIndicatorPopupContent()
    {
    AKNTASHOOK_REMOVE();
    // Ensure here that this popup is removed from the global
    // popup priority stack.
    AknGlobalPopupPriorityController::RemovePopupPriority( *this );

    iItems.ResetAndDestroy();
    iItems.Close();

    delete iClock;
    delete iBattery;
    delete iSignal;

    if ( iBatteryPlugin || iSignalPlugin )
        {
        delete iBatteryPlugin;
        delete iSignalPlugin;
        REComSession::FinalClose();
        }

    MTouchFeedback* feedback = MTouchFeedback::Instance();
    if ( feedback )
        {
        feedback->RemoveFeedbackForControl( this );
        }

    delete iSeparatorIcon;
    }


// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::MinimumSize
// -----------------------------------------------------------------------------
//
TSize CAknIndicatorPopupContent::MinimumSize()
    {
    TInt items( iItems.Count() );

    // Get the maximum number of indicator items from layout data.
    TAknLayoutScalableParameterLimits limits(
        AknLayoutScalable_Avkon::list_single_uniindi_pane_ParamLimits() );
    TInt variety( limits.LastRow() + 1 ); // Last variety for no indicator items

    if ( items > 0 )
        {
        if ( items <= variety )
            {
            variety -= items;
            }
        else
            {
            variety = 0; // Maximum number of items.
            }
        }

    TRect applicationWindow;
    AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EApplicationWindow,
                                       applicationWindow );

    TAknLayoutRect layoutRect;
    layoutRect.LayoutRect(
        applicationWindow,
        AknLayoutScalable_Avkon::popup_uni_indicator_window( variety ) );

    return TSize( layoutRect.Rect().Size() );
    }


// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::HandleResourceChange
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::HandleResourceChange( TInt aType )
    {
    CCoeControl::HandleResourceChange( aType );

    TInt  itemCount( iItems.Count() );

    switch ( aType )
        {
        case KAknsMessageSkinChange:
            {
            // Update clock text color.
            TRgb textColor;

            MAknsSkinInstance* skin = AknsUtils::SkinInstance();

            AknsUtils::GetCachedColor( skin,
                                       textColor,
                                       KAknsIIDQsnTextColors,
                                       EAknsCIQsnTextColorsCG55 );

            iClock->SetColor( textColor );

            // Update signal icon.
            iSignal->SetColorIndex( 0 ); // reset skin color
            TRAP_IGNORE( SetSignalStateL( iSignalState ) );
            // Color of the battery indicator is changed by it's container.
            iBattery->HandleResourceChange( aType );
            // Update the signal icon and separator line color.
            TRAP_IGNORE( LoadIconsL() );

            // Update the color of the indicator icons.
            for ( TInt i = 0; i < itemCount; i++ )
                {
                TRAP_IGNORE( iItems[i]->UpdateIndicatorIconL() );
                }
            break;
            }

        case KEikMessageFadeAllWindows:
            {
            if ( !Window().IsFaded() )
                {
                Window().SetFaded( ETrue,
                                   RWindowTreeNode::EFadeIncludeChildren );
                }
            break;
            }

        case KEikMessageUnfadeWindows:
            {
            if ( Window().IsFaded() )
                {
                Window().SetFaded( EFalse,
                                   RWindowTreeNode::EFadeIncludeChildren );
                }
            break;
            }

        default:
            break;
        }
    }


// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::HandlePointerEventL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::HandlePointerEventL(
    const TPointerEvent& aPointerEvent )
    {
    if ( Rect().Contains( aPointerEvent.iPosition ) )
        {
        CBase* currentFocusedItem = NULL;
        switch ( aPointerEvent.iType )
            {
            case TPointerEvent::EButton1Down:
                // Set flag that down was inside the popup.
                iFlags |= EAknIndicatorPopupContentButton1Down;
                iPreviousPressedDownItem = NULL;
                
                if ( iClock->Rect().Contains( aPointerEvent.iPosition ))
                    {
                    currentFocusedItem = iClock;
                    iPressedDownRect = iClockPressedDownArea;
                    }
                else if ( iSignalArea.Contains( aPointerEvent.iPosition ))
                    {
                    currentFocusedItem = iSignal;
                    iPressedDownRect = iSignalPressedDownArea;
                    }
                else if ( iBatteryArea.Contains( aPointerEvent.iPosition ))
                    {
                    currentFocusedItem = iBattery;
                    iPressedDownRect = iBatteryArea;
                    }
                else
                    {
                    CAknIndicatorPopupItem* item = TappedItem( aPointerEvent.iPosition );
                    if ( item )
                        {
                        currentFocusedItem = item;
                        iPressedDownRect = item->iRect;
                        }
                    else
                    // No item get tapped
                        {                        
                        return;
                        }                   
                    }
                iEnablePressedDownState = ETrue;
                Window().Invalidate( iPressedDownRect );                
                iPreviousPressedDownItem = currentFocusedItem;
                break;    

            case TPointerEvent::EDrag:
                if ( iClock->Rect().Contains( aPointerEvent.iPosition ))
                    {
                    currentFocusedItem = iClock;
                    }
                else if ( iSignalArea.Contains( aPointerEvent.iPosition ))
                    {
                    currentFocusedItem = iSignal;
                    }
                else if ( iBatteryArea.Contains( aPointerEvent.iPosition ))
                    {
                    currentFocusedItem = iBattery;
                    }
                else
                    {
                    currentFocusedItem = TappedItem( aPointerEvent.iPosition );
                    }
  
                if( currentFocusedItem != iPreviousPressedDownItem )
                    {
                    iFlags &= ~EAknIndicatorPopupContentButton1Down;
                    iEnablePressedDownState = EFalse;
                    Window().Invalidate( iPressedDownRect );                    
                    }
                break;

            case TPointerEvent::EButton1Up:
                if ( iFlags & EAknIndicatorPopupContentButton1Down )
                    {
                    // Up happened, reset button down flag.
                    iFlags &= ( ~EAknIndicatorPopupContentButton1Down );
                    iEnablePressedDownState = EFalse;

                    if ( iBatteryPlugin &&
                         iBatteryArea.Contains( aPointerEvent.iPosition ) )
                        {
                        ReportEventL(
                            static_cast<MCoeControlObserver::TCoeEvent>(
                                EAknIndicatorPopupTapEvent ) );
                            
                        // Dummy value used as indicator UID parameter,
                        // as it's not used by the battery indicator plugin.
                        iBatteryPlugin->HandleIndicatorTapL( 0 );
                        break;
                        }
                    else if ( iSignalPlugin &&
                         iSignalArea.Contains( aPointerEvent.iPosition ) )
                        {
                        ReportEventL(
                            static_cast<MCoeControlObserver::TCoeEvent>(
                                EAknIndicatorPopupTapEvent ) );

                        // Dummy value used as indicator UID parameter,
                        // as it's not used by the signal indicator plugin.
                        iSignalPlugin->HandleIndicatorTapL( 0 );
                        break;
                        }
                    else if ( iClock &&
                              iClock->Rect().Contains( aPointerEvent.iPosition ) )
                        {
                        ReportEventL(
                            static_cast<MCoeControlObserver::TCoeEvent>(
                                EAknIndicatorPopupTapEvent ) );

                        if ( !( iFlags & EAknIndicatorPopupContentClockBeingLaunched ) )
                            {
                            // Start clock application directly when down event
                            // happens in clock, this functionality was previously
                            // in the CAknDigitalClock.
                            RApaLsSession apa;
                            User::LeaveIfError( apa.Connect() );
                            CleanupClosePushL( apa );
                            CApaCommandLine* cmdLine = CApaCommandLine::NewLC();
                            cmdLine->SetExecutableNameL( KClockApplication );
                            cmdLine->SetCommandL( EApaCommandRun );
                            User::LeaveIfError( apa.StartApp( *cmdLine ) );
                            CleanupStack::PopAndDestroy( 2, &apa ); // cmdLine and apa

                            // Set the flag indicating that clock is being launched.
                            iFlags |= EAknIndicatorPopupContentClockBeingLaunched;
                            }
                        }

                    CAknIndicatorPopupItem* item = TappedItem( aPointerEvent.iPosition );
                    if ( item )
                        {
                        ReportEventL(
                            static_cast<MCoeControlObserver::TCoeEvent>(
                                EAknIndicatorPopupTapEvent ) );
                        
                        item->iPlugin->HandleIndicatorTapL( item->iUid );
                        break;
                        }
                    }
                break;

            default:
                break;
            }
        }
    else if ( iFlags & EAknIndicatorPopupContentButton1Down )// Drag to outside
        {
        iFlags &= ~EAknIndicatorPopupContentButton1Down;
        iEnablePressedDownState = EFalse;
        Window().Invalidate( iPressedDownRect );
        }
    }


// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::CountComponentControls
// -----------------------------------------------------------------------------
//
TInt CAknIndicatorPopupContent::CountComponentControls() const
    {
    TInt count = 0;

    if ( iClock )
        {
        count++;
        }

    if ( iBattery )
        {
        count++;
        }

    if ( iSignal )
        {
        count++;
        }

    return count;
    }


// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::ComponentControl
// -----------------------------------------------------------------------------
//
CCoeControl* CAknIndicatorPopupContent::ComponentControl( TInt aIndex ) const
    {
    CCoeControl* componentControl = NULL;

    switch ( aIndex )
        {
        case 0:
            componentControl = iClock;
            break;
        case 1:
            componentControl = iBattery;
            break;
        case 2:
            componentControl = iSignal;
            break;
        default:
            break;
        }

    return componentControl;
    }


// -----------------------------------------------------------------------------
// From class CCoeControl.
// CAknIndicatorPopupContent::SetContainerWindowL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::SetContainerWindowL(
    const CCoeControl& aContainer )
    {
    CCoeControl::SetContainerWindowL( aContainer );

    if( iClock )
        {
        iClock->SetContainerWindowL( aContainer );
        }
    }


// ---------------------------------------------------------------------------
// CAknIndicatorPopupContent::AddItemL
// ---------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::AddItemL( TInt aIndicatorUid,
                                          const TDesC& aText,
                                          TInt aTextType,
                                          const CGulIcon* aIcon,
                                          CAknIndicatorPlugin* aPlugin,
                                          TInt aPriority )
    {
    TInt  itemCount( iItems.Count() );
    for ( TInt i = 0; i < itemCount; i++ )
        {
        if ( iItems[i]->iUid == aIndicatorUid )
            {
            // Indicator item exists already.
            return;
            }
        }

    CAknIndicatorPopupItem* newItem =
        new (ELeave) CAknIndicatorPopupItem( aIndicatorUid,
                                             aPlugin,
                                             aPriority,
                                             aTextType );

    if ( aIcon )
        {
        // If the plugin provides an icon, use it.
        newItem->ConstructL( aText, aIcon );
        }
    else
        {
        // Otherwise use the same icon as in the indicator pane.
        TInt bitmapID;
        TInt maskID;

        CAknIndicator::GetBitmapIndexL(
            CAknIndicatorContainer::EUniversalIndicators,
            aIndicatorUid,
            bitmapID,
            maskID );

        newItem->ConstructL( aText, bitmapID, maskID );
        }

    if ( aPlugin )
        {
        aPlugin->SetPluginObserver( this );
        }

    iItems.AppendL( newItem );

    // Discard possible pressed down highlight when the popup size changes,
    // as the position of the highlighted item may change.
    iEnablePressedDownState = EFalse;
    
    SizeChanged();
    }


// ---------------------------------------------------------------------------
// CAknIndicatorPopupContent::RemoveItem
// ---------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::RemoveItem( TInt aIndicatorUid )
    {
    TInt itemCount( iItems.Count() );
    for ( TInt i = 0; i < itemCount; i++ )
        {
        if ( iItems[i]->iUid == aIndicatorUid )
            {
            delete iItems[i];
            iItems[i] = NULL;
            iItems.Remove( i );
            break;
            }
        }

    // Discard possible pressed down highlight when the popup size changes,
    // as the position of the highlighted item may change.
    iEnablePressedDownState = EFalse;
    
    SizeChanged();
    }


// ---------------------------------------------------------------------------
// CAknIndicatorPopupContent::SetContentVisible
// ---------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::SetContentVisible( TBool aVisible )
    {
    if ( aVisible )
        {
        SizeChanged(); // also includes UpdateFeedbackAreas()
        }
    if ( iClock )
        {
        // The clock timer needs to be running only when the
        // popup is visible.
        if ( aVisible )
            {
            // Error ignored, if this leaves then the clock
            // is not updated if time changes while popup is visible.
            iClock->StartTimer();
            }
        else
            {
            iClock->StopTimer();
            }
        }


    if ( !aVisible )
        {
        // Remove this popup from the global popup priority stack.
        AknGlobalPopupPriorityController::RemovePopupPriority( *this );

        // Clear the state flags.
        iFlags &= ( ~EAknIndicatorPopupContentButton1Down );
        iFlags &= ( ~EAknIndicatorPopupContentClockBeingLaunched );
        }
    else
        {
        // Add this popup from the global popup priority stack so
        // that it can receive key events.
        // Note that the popup priority is not set, so it has
        // priority the default priority, which is 0.
        AknGlobalPopupPriorityController::ShowPopup( *this, ETrue );
        }
    }

// ---------------------------------------------------------------------------
// CAknIndicatorPopupContent::SetBatteryStateL
// ---------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::SetBatteryStateL( TInt aState )
    {
    if ( iBattery )
        {
        iBattery->SetBatteryIconL( aState );
        }
    }
// ---------------------------------------------------------------------------
// CAknIndicatorPopupContent::SetSignalStateL
// ---------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::SetSignalStateL( TInt aState )
    {
    TInt newState( aState );
    
    if ( iSignal )
        {
        if ( newState == KAknSignalOffLineMode )
            {
            // Set offline state icon to the signal area. 
            iSignal->SetOffLine( ETrue );
            }
        else if ( newState == KAknSignalOffLineMode - 1 )
            {
            // This aState parameter is used to inform about connection
            // state change from offline mode. Signal icon is set not
            // to be in offline state and the correct signal state is
            // used from iSignalState.
            iSignal->SetOffLine( EFalse );
            newState = iSignalState;
            }
        iSignal->LoadIconL( newState, EAknsCIQsnIconColorsCG5 );
        }

    // Ensure that the new value is valid, offline mode state is
    // not stored as a signal state.
    if ( newState >= 0 &&
         newState <= EAknSignalHsdpaIndicatorMultipdp ) // last signal state
        {
        iSignalState = newState;
        }
    }
// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::HandlePluginUpdateL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::HandlePluginUpdateL( TInt aUid )
    {
    TInt  itemCount( iItems.Count() );
    for ( TInt i = 0; i < itemCount; i++ )
        {
        if ( iItems[i]->iUid == aUid &&
             iItems[i]->iPlugin ) // Can update only if we have a
            {                     // pointer to the plugin.
            // Update text
            TInt textType;
            HBufC* text = iItems[i]->iPlugin->TextL( aUid, textType );
            if ( text )
                {
                CleanupStack::PushL( text );
                iItems[i]->UpdateTextL( *text, textType );
                CleanupStack::PopAndDestroy( text );
                }

            // Update icon
            const CGulIcon* icon = NULL;
            TRAPD( err, icon = iItems[i]->iPlugin->IconL( aUid ) );
            if ( !err && icon )
                {
                iItems[i]->UpdateIndicatorIconL( icon );
                }

            break;
            }
        }

    DrawDeferred();
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::UpdateAllIndicatorsL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::UpdateAllIndicatorsL()
    {
    CAknIndicatorPlugin* plugin = NULL;
    TInt  itemCount( iItems.Count() );

    for ( TInt i = 0; i < itemCount; i++ )
        {
        plugin = iItems[i]->iPlugin;

        if ( plugin ) // Can update only if we have a
            {         // pointer to the plugin.
            TInt uid( iItems[i]->iUid );

            // Update text
            TInt textType;
            HBufC* text = plugin->TextL( uid, textType );
            if ( text )
                {
                CleanupStack::PushL( text );
                iItems[i]->UpdateTextL( *text, textType );
                CleanupStack::PopAndDestroy( text );
                }

            // Update icon
            const CGulIcon* icon = NULL;
            TRAPD( err, icon = plugin->IconL( uid ) );
            if ( !err && icon )
                {
                iItems[i]->UpdateIndicatorIconL( icon );
                }
            }
        }
    }


// -----------------------------------------------------------------------------
// Default constructor.
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupContent::CAknIndicatorPopupContent()
    : iItems( KItemArrayGranularity )
    {
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::ConstructL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::ConstructL()
    {
    iClock = CAknDigitalClock::NewL( this );
    TAknTextLineLayout clockLayout(
        AknLayoutScalable_Avkon::uniindi_top_pane_t1( 0 ).LayoutLine() );

    // Ensure that the left and right values of the clock text layout
    // are zero, currently the whole top area is set as the parent
    // for this layout, so it's narrowed by reducting the
    // signal and battery pane areas.
    clockLayout.il = 0;
    clockLayout.ir = 0;
    iClock->SetTimeLayout( clockLayout );
    TRgb textColor;

    MAknsSkinInstance* skin = AknsUtils::SkinInstance();

    AknsUtils::GetCachedColor( skin,
                               textColor,
                               KAknsIIDQsnTextColors,
                               EAknsCIQsnTextColorsCG55 );
    iClock->SetColor( textColor );

    iBattery = CAknBatteryIcon::NewL();
    iBattery->SetContainerWindowL( *this );
    iSignal = CAknSignalIcon::NewL();
    iSignal->SetContainerWindowL( *this );

    // Create default signal icon.
    iSignal->LoadIconL( 0, EAknsCIQsnIconColorsCG5 );

    // Set this to draw the icon also in offline mode.
    iSignal->SetOffLineIconDraw( ETrue );
    TRAPD( err, iBatteryPlugin = CAknIndicatorPlugin::NewL(
        TUid::Uid( KImplUIDBatteryIndicatorPlugin ) ) );
    if ( err == KErrNone )
        {
        iBatteryPlugin->SetPluginObserver( this );
        }
    TRAPD( err1, iSignalPlugin  = CAknIndicatorPlugin::NewL(
        TUid::Uid( KImplUIDSignalIndicatorPlugin ) ) );
    if ( err1 == KErrNone )
        {
        iSignalPlugin->SetPluginObserver( this );
        }

    iFeedback = MTouchFeedback::Instance();

    // Load the separator line and signal icon graphics.
    LoadIconsL();
    
    iEnablePressedDownState = EFalse;
    iPreviousPressedDownItem = NULL;
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::PrioritizeIndicatorsL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::PrioritizeIndicatorsL()
    {
    TInt count = iItems.Count();
    if ( count < 2 )
        {
        return;
        }

    CAknIndicatorPopupItem* temp;
    for ( TInt ii = 1; ii < count; ii++ )
        {
        temp = iItems[ii];
        TInt tempPriority = temp->iPriority;

        if ( tempPriority >= 0 )
            {
            for ( TInt jj = 0; jj <= ii; jj++ )
                {
                TInt indicatorPriority = iItems[jj]->iPriority;

                if ( tempPriority < indicatorPriority )
                    {
                    iItems.Remove( ii );
                    iItems.InsertL( temp, jj );
                    break;
                    }
                else if ( jj == ( ii - 1 ) )
                    {
                    break;
                    }
                }
            }
        }
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::UpdateFeedbackAreas
//
// Digital clock takes care of its own feedback, but we have to either
// enable or block feedback for signal indicator, according to
// wheather it has associated plug-in or not (if it has plug-in, then
// something happens when it is tapped and feedback should be produced).
//
// In addition we have to set feedback to every item, which acts as a link.
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::UpdateFeedbackAreas()
    {
    if ( iFeedback )
        {
        // First remove all old feedback areas
        iFeedback->RemoveFeedbackForControl( this );
        if ( iBatteryPlugin )
            {
            // Set battery indicator's feedback area    
            CFeedbackSpec* fbSpec = CFeedbackSpec::New();
            if ( fbSpec )
                {
                fbSpec->AddFeedback( ETouchEventStylusDown,
                                     ETouchFeedbackSensitiveButton );
                fbSpec->AddFeedback( ETouchEventStylusUp,
                                     ETouchFeedbackSensitiveButton,
                                     ETouchFeedbackVibra );

                iFeedback->SetFeedbackArea( this, 
                                            KBatteryAreaIndex, 
                                            iBatteryArea, 
                                            fbSpec );
                delete fbSpec;                                
                }
            }
            
        if ( iSignalPlugin )
            {
            // Set signal indicator's feedback area
            CFeedbackSpec* fbSpec = CFeedbackSpec::New();
            if ( fbSpec )
                {
                fbSpec->AddFeedback( ETouchEventStylusDown,
                                     ETouchFeedbackSensitiveButton );
                fbSpec->AddFeedback( ETouchEventStylusUp,
                                     ETouchFeedbackSensitiveButton,
                                     ETouchFeedbackVibra );

                iFeedback->SetFeedbackArea( this, 
                                            KSignalAreaIndex, 
                                            iSignalArea, 
                                            fbSpec );
                delete fbSpec;                                
                }
            }

        // Go through the items, and set feedback areas for those which act
        // as link (i.e. have associated plugin)
        TInt itemCount( iItems.Count() );

        for ( TInt i = 0; i < itemCount; i++ )
            {
            if ( iItems[i]->iPlugin &&
                 iItems[i]->iTextType ==
                     CAknIndicatorPlugin::EAknIndicatorPluginLinkText &&
                 iItems[i]->iRect != TRect( 0, 0, 0, 0 ) )
                {
                CFeedbackSpec* fbSpec = CFeedbackSpec::New();
                if ( fbSpec )
                    {
                    fbSpec->AddFeedback( ETouchEventStylusDown,
                                         ETouchFeedbackSensitiveButton );

                    iFeedback->SetFeedbackArea( this,
                                                KSignalAreaIndex + i + 1,
                                                iItems[i]->iRect,
                                                fbSpec );
                    delete fbSpec;                                
                    }
                }
            }
        }
    }


// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::LoadIconsL
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::LoadIconsL()
    {
    TRect rect( Rect() );

    delete iSeparatorIcon;
    iSeparatorIcon = NULL;

    CFbsBitmap* bitmap = NULL;
    CFbsBitmap* mask   = NULL;

    AknsUtils::CreateColorIconL( AknsUtils::SkinInstance(),
                                 KAknsIIDQgnGrafLineVerticalFade,
                                 KAknsIIDQsnLineColors,
                                 EAknsCIQsnLineColorsCG3,
                                 bitmap,
                                 mask,
                                 KNullDesC,
                                 KErrNotFound,
                                 KErrNotFound,
                                 KRgbGray );

    iSeparatorIcon = CGulIcon::NewL( bitmap, mask );

    TAknLayoutRect layoutRect;
    layoutRect.LayoutRect(
        rect,
        AknLayoutScalable_Avkon::uniindi_top_pane() );

    layoutRect.LayoutRect(
        layoutRect.Rect(),
        AknLayoutScalable_Avkon::uniindi_top_pane_g3( 1 ) );

    AknIconUtils::SetSize( iSeparatorIcon->Bitmap(),
                           layoutRect.Rect().Size(),
                           EAspectRatioNotPreserved );

    //
    // Create a custom signal icon for connectivity manager link.
    //

    CFbsBitmap* signalBitmap = NULL;
    CFbsBitmap* signalMask   = NULL;

    AknsUtils::CreateColorIconL( AknsUtils::SkinInstance(),
                                 KAknsIIDQgnPropLinkConnectionManager,
                                 KAknsIIDQsnIconColors,
                                 EAknsCIQsnIconColorsCG5,
                                 signalBitmap,
                                 signalMask,
                                 AknIconUtils::AvkonIconFileName(),
                                 EMbmAvkonQgn_prop_link_connection_manager,
                                 EMbmAvkonQgn_prop_link_connection_manager_mask,
                                 KRgbGray );

    // Ownership is transferred.
    iSignal->SetSignalIcon( signalBitmap );
    iSignal->SetSignalIconMask( signalMask );
    }

// -----------------------------------------------------------------------------
// CAknIndicatorPopupContent::TappedItem
// -----------------------------------------------------------------------------
//
CAknIndicatorPopupItem* 
CAknIndicatorPopupContent::TappedItem(const TPoint& aPoint) const
    {    
    for ( TInt ii = 0; ii < iItems.Count(); ii++ )
        {
        CAknIndicatorPopupItem* item = iItems[ii];                    
        if ( item->iRect.Contains( aPoint ) && item->iPlugin
             && item->iTextType ==
             CAknIndicatorPlugin::EAknIndicatorPluginLinkText )
            {
            return item;
            }                        
        }
    return NULL;
    }

// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::SizeChanged
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::SizeChanged()
    {
    TRect rect( Rect() );

    // Popup window top area
    TAknLayoutRect layoutRect;
    layoutRect.LayoutRect( rect,
                           AknLayoutScalable_Avkon::uniindi_top_pane() );
    TRect topRect( layoutRect.Rect() );

    // Signal pane area
    layoutRect.LayoutRect( topRect,
                           AknLayoutScalable_Avkon::uniindi_top_pane_g1() );
    TRect signalRect( layoutRect.Rect() );

    // Battery pane area
    layoutRect.LayoutRect( topRect,
                           AknLayoutScalable_Avkon::uniindi_top_pane_g2() );
    TRect batteryRect( layoutRect.Rect() );
    
    // Clock pane area
    layoutRect.LayoutRect( topRect,
                           AknLayoutScalable_Avkon::aid_area_touch_clock() );
    TRect clockRect( layoutRect.Rect() );

    // Separator line size
    layoutRect.LayoutRect(
        topRect,
        AknLayoutScalable_Avkon::uniindi_top_pane_g3() );
    TRect separatorRect = layoutRect.Rect();
    TSize separatorSize( layoutRect.Rect().Size() );

    TBool layoutMirrored( AknLayoutUtils::LayoutMirrored() );

    iClock->SetRect( clockRect );
    iBattery->SetRect( batteryRect );
    iSignal->SetRect( signalRect );

    // Make the touch responsive areas of signal pane
    // slightly larger by not taking into account the margins from
    // top and sides.
    iBatteryArea = batteryRect;
    iSignalArea  = signalRect;

    if ( layoutMirrored )
        {
        iBatteryArea.iTl    = topRect.iTl;
        iBatteryArea.iBr.iY = topRect.iBr.iY;
        iSignalArea.iBr     = topRect.iBr;
        iSignalArea.iTl.iY  = topRect.iTl.iY;
        }
    else
        {
        iBatteryArea.iBr    = topRect.iBr;
        iBatteryArea.iTl.iY = topRect.iTl.iY;
        iBatteryArea.iTl.iX = clockRect.iBr.iX;
        iSignalArea.iTl     = topRect.iTl;
        iSignalArea.iBr.iY  = topRect.iBr.iY;
        }

    // Calculate permanent component press down effect rect
    iSignalPressedDownArea = iSignalArea;
    iClockPressedDownArea = clockRect;
        
    // Indicator area
    layoutRect.LayoutRect( rect,
                           AknLayoutScalable_Avkon::list_uniindi_pane() );
    TRect listRect( layoutRect.Rect() );

    TRAP_IGNORE( PrioritizeIndicatorsL() );

    TAknLayoutScalableParameterLimits limits(
        AknLayoutScalable_Avkon::list_single_uniindi_pane_ParamLimits() );
    TInt maxNumberOfIndicatorsShown = limits.LastRow();

    TInt itemCount( iItems.Count() );

    for ( TInt i = 0; i < itemCount; i++ )
        {
        if ( i <= maxNumberOfIndicatorsShown )
            {
            layoutRect.LayoutRect(
                listRect,
                AknLayoutScalable_Avkon::list_single_uniindi_pane( 0, 0, i ) );
            iItems[i]->SetRect( layoutRect.Rect() );
            }
        else
            {
            iItems[i]->SetRect( TRect( 0, 0, 0, 0 ) );
            }
        }

    UpdateFeedbackAreas();

    AknIconUtils::SetSize( iSeparatorIcon->Bitmap(),
                           separatorSize,
                           EAspectRatioNotPreserved );

    DrawDeferred();
    }


// -----------------------------------------------------------------------------
// From class CCoeControl
// CAknIndicatorPopupContent::Draw
// -----------------------------------------------------------------------------
//
void CAknIndicatorPopupContent::Draw( const TRect& aRect ) const
    {
    if ( AknLayoutUtils::PenEnabled() )
        {
        CWindowGc& gc = SystemGc();

        MAknsSkinInstance* skin = AknsUtils::SkinInstance();

        TRect rect( Rect() );

        TAknLayoutRect layoutRect;
        layoutRect.LayoutRect(
            rect,
            AknLayoutScalable_Avkon::uniindi_top_pane() );
        TRect topRect( layoutRect.Rect() );

        layoutRect.LayoutRect(
            topRect,
            AknLayoutScalable_Avkon::bg_uniindi_top_pane( 1 ) );
        TRect topCenterRect( layoutRect.Rect() );
        layoutRect.LayoutRect(
            topRect,
            AknLayoutScalable_Avkon::uniindi_top_pane_g3() );
        TRect leftSeparatorRect( layoutRect.Rect() );
        
        layoutRect.LayoutRect(
            topRect,
            AknLayoutScalable_Avkon::uniindi_top_pane_g3( 1 ) );
        TRect separatorRect( layoutRect.Rect() );
       
        layoutRect.LayoutRect(
            topRect,
            AknLayoutScalable_Avkon::uniindi_top_pane_g4() );
        TRect rightSeparatorRect( layoutRect.Rect() );

        // Draw the button frame to the permanent items area.
        AknsDrawUtils::DrawFrame( skin,
                                  gc,
                                  topRect,
                                  topCenterRect,
                                  KAknsIIDQsnFrPopupHeading,
                                  KAknsIIDQsnFrPopupHeadingCenter );
        if ( iEnablePressedDownState )
            {  
            AknsDrawUtils::DrawFrame( skin,
                                      gc,
                                      iPressedDownRect,
                                      iPressedDownRect,
                                      KAknsIIDQsnFrListPressed,
                                      KAknsIIDQsnFrListCenterPressed);
            }
        CFbsBitmap* bitmap = iSeparatorIcon->Bitmap();
        CFbsBitmap* mask   = iSeparatorIcon->Mask();

        // Draw the separator line on the top pane.
        gc.BitBltMasked( leftSeparatorRect.iTl,
                         bitmap,
                         leftSeparatorRect.Size(),
                         mask,
                         ETrue );
                          
        gc.BitBltMasked( rightSeparatorRect.iTl,
                         bitmap,
                         rightSeparatorRect.Size(),
                         mask,
                         ETrue );

        // Draw the indicator items.
        for ( TInt i = 0; i < iItems.Count(); i++ )
            {
            if ( aRect.Intersects( iItems[i]->iRect ) )
                {
                iItems[i]->DrawItem( gc );
                }
            }
        }
    }
