idlehomescreen/xmluicontroller/src/contentrenderer.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:40:49 +0200
changeset 0 f72a12da539e
child 4 4d54b72983ae
permissions -rw-r--r--
Revision: 200949 Kit: 200951

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

// System includes
#include    <e32hashtab.h>
#include    <utf.h>
#include    <gulicon.h>

// User includes

#include    "contentrenderer.h"
#include    "appui.h"
#include    "activetransactionstack.h"
#include    "transactionfactoryimpl.h"
#include    "mtransaction.h"
#include    "mtransactionelement.h"
#include    "aixmluiutils.h"
#include    "xmluicontroller.h"
#include    "xmlnodeidgenerator.h"
#include    "aixmluiconstants.h"
#include    "aifweventhandler.h"
#include    "databuffertransactionelement.h"
#include    "newstickercallbackhandler.h"
#include    "newstickertransactionelement.h"
#include    "csspropertymap.h"
#include    "policyevaluator.h"
#include    "debug.h"
#include    "xnuiengineappif.h"
#include    "xnnodeappif.h"
#include    "xnproperty.h"
#include    "mxncomponentinterface.h"
#include    "xntext.h"
#include    "xnbitmap.h"
#include    "xntype.h"
#include    "xnmenuadapter.h"
#include    "xnlistquerydialogadapter.h"
#include    "mxncomponentinterface.h"
#include    "aistrcnv.h"
#include    "contentprioritymap.h"
#include    "ainativeuiplugins.h"

using namespace AiXmlUiController;
using namespace AiUiDef::xml;
using namespace XnTextInterface;
using namespace XnImageInterface;

namespace
    {
    typedef RPointerArray<CXnNodeAppIf> RXnNodeArray;

    /**
     * Gets content item iterator.
     *
     * @param aPlugin plugin to fetch the iterator
     * @param aContentType type of iterator (content/resource/event)
     * @return the iterator
     * @leave KErrNotSupported if plugin does not provide iterator
     */ 
    static MAiContentItemIterator& ContentItemIteratorL( MAiPropertyExtension& aPlugin,
                                                         TInt aContentType )
        {
        MAiContentItemIterator* iter =
            static_cast< MAiContentItemIterator* >( aPlugin.GetPropertyL( aContentType ) );

        if ( !iter )
            {
            User::Leave( KErrNotSupported );
            }

        return *iter;
        }

    /**
     * Gets the content priority associated in the property element.
     *
     * @param aPropertyElement the property element
     * @return priority value or KErrNotFound for non-prioritized element
     */
    TInt GetContentPriority( CXnNodeAppIf& aPropertyElement )
        {
        TInt32 priority( KErrNotFound );
        
        const TDesC8* name = PropertyValue( aPropertyElement,
                                            property::KName );
                                            
        
        if ( name && *name == name::KPriority )
            {
            const TDesC8* value = PropertyValue( aPropertyElement,
                                                 property::KValue );
            if ( value )
                {
                AiUtility::ParseInt( priority, *value );
                }
            }
            
        return priority;
        }
    
    /**
     * Remove elements from array which do not contain priority or priority is not lower than
     * the given value.
     * 
     * @param aElements array of elements
     * @param aLastPriority upper bound of priority value (excluded from the array)
     */
    void RemoveNonPriorityElements( RPointerArray< CXnNodeAppIf>& aElements,
                                    TInt aLastPriority )
        {
        //  Remove non priority elements and higher priority elements

        TInt elementCount = aElements.Count();    
        for ( TInt i = 0; i < elementCount; )
            {
            CXnNodeAppIf* current = aElements[i];
            
            // Check name attribute
            const TDesC8* name = PropertyValue( *current, property::KName );
            
            if ( !name || *name != name::KPriority )
                {
                // Remove current
                aElements.Remove( i );
                --elementCount;
                }
            else
                {
                // Check current priority
                const TDesC8* value = PropertyValue( *current, property::KValue );
                
                if ( !value ) // value not present
                    {
                    aElements.Remove( i );
                    --elementCount;
                    continue;
                    }
                
                TInt32 currentPriority( KErrNotFound );
                if ( AiUtility::ParseInt( currentPriority, *value ) != KErrNone )
                    {
                    // value is not integer
                    aElements.Remove( i );
                    --elementCount;
                    continue;
                    }
                    
                if ( currentPriority < aLastPriority )
                    {
                    // Keep element and iterate further
                    ++i;
                    }
                else
                    {
                    // priority is too high
                    aElements.Remove( i );
                    --elementCount;
                    }
                }
            }
        }
    
    /**
     * Descending priority order for prioritized content selectors.
     *
     * @param aNode1 First node to compare
     * @param aNode2 Second node to compare
     * @return 0 nodes have equal priority
     * @return >0 aNode1 has lower priority
     * @return <0 aNode2 has lower priority
     */
    TInt DescendingPriorityOrder( const CXnNodeAppIf& aNode1,
                                  const CXnNodeAppIf& aNode2 )
        {
        // Array content has been validated, so no checks are needed
        const TDesC8* value1 = PropertyValue( aNode1,
                                              property::KValue );
        
        const TDesC8* value2 = PropertyValue( aNode2,
                                              property::KValue );
        
        TInt32 priority1( KErrNotFound );
        AiUtility::ParseInt( priority1, *value1 );
        
        TInt32 priority2( KErrNotFound );
        AiUtility::ParseInt( priority2, *value2 );
        
        if ( priority1 == priority2 )
            {
            return 0;
            }
            
        return ( priority1 < priority2 ) ? 1 : -1;
        }
    
    /**
     * Removes duplicate entries in content change array
     */
    void RemoveDuplicateContentChangesL( RAiPolicyElementArray& aArray )
        {
        for ( TInt i = 0; i < aArray.Count(); ++i )
            {
            HBufC* id = PropertyValueL( aArray[i].Target(), 
                                            AiUiDef::xml::property::KId );
            if ( id )
                {
                CleanupStack::PushL( id );
                for ( TInt j = i; j < aArray.Count(); ++j )
                    {
                    HBufC* id2 = PropertyValueL( aArray[j].Target(), 
                                                           AiUiDef::xml::property::KId );
                    if ( id2)
                        {
                        CleanupStack::PushL( id2 );
                        // Same id and same policy
                        if ( i != j &&
                             id->Compare( *id2 ) == 0 && 
                             ( aArray[i].Policy().Compare( aArray[j].Policy()) == 0) 
                                )
                            {
                            aArray.Remove( j );
                            --j;
                            }
                        }
                    CleanupStack::PopAndDestroy( id2 );
                    
                    }
                CleanupStack::PopAndDestroy( id );
                }
            }
        }
    /**
     * Cleanup item for cleanup of TPtrHashMapIter
     */            
    class TMapCleanupItem
        {
    public:
        
        TMapCleanupItem( TPtrHashMapIter< TDesC, TInt >& aIterator );
        
        /**
         * Removes the pointers in the map and deletes the objects
         * referenced by the pointers.
         */ 
        void Release();
                
    private:
            
        TPtrHashMapIter< TDesC, TInt > iIterator;
        
        };

    /**
     * Helper to handle cleanup of map iterator
     * @param aMapCleanupItem 
     */
    void CleanupRelease( TAny* aMapCleanupItem )
        {
        if ( aMapCleanupItem )
            {
            static_cast< TMapCleanupItem* >( aMapCleanupItem )->Release();
            }
        }

    /**
     * Helper to push map iterator into cleanup stack.
     *
     * @param aCleanupItem item to push into cleanup stack
     **/
    void CleanupReleasePushL( TMapCleanupItem& aCleanupItem )
        {
        CleanupStack::PushL( TCleanupItem( CleanupRelease,
                                           &aCleanupItem ) );
        }

    
    }

TMapCleanupItem::TMapCleanupItem( TPtrHashMapIter< TDesC, TInt >& aIterator )
    : iIterator( aIterator )
    {
    }

void TMapCleanupItem::Release()
    {
    // Delete current key and value
    const TDesC* key = iIterator.CurrentKey();
    const TInt* value = iIterator.CurrentValue();
    
    delete key;
    delete value;
    
    // Remove mapping from the map.
    iIterator.RemoveCurrent();
    }



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

CContentRenderer::CContentRenderer( CAppUi& aAppUi )
    : iAppUi( aAppUi )
    {
    }

void CContentRenderer::ConstructL()
    {
    iContentPriorityMap = AiUtility::CContentPriorityMap::NewL();
    iPropertyMap = CCssPropertyMap::NewL();
    iFactory = CTransactionFactoryImpl::NewL(*iContentPriorityMap,
                                             *iPropertyMap);
    iStack = CActiveTransactionStack::NewL();
    iNodeIdGenerator = CXmlNodeIdGenerator::NewL();
    iTimer = CPeriodic::NewL( CActive::EPriorityStandard );
    iPolicyEvaluator = CPolicyEvaluator::NewL();
    }

CContentRenderer* CContentRenderer::NewL( CAppUi& aAppUi )
    {
    CContentRenderer* self = new( ELeave ) CContentRenderer( aAppUi );

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

    return self;
    }

CContentRenderer::~CContentRenderer()
    {
    delete iPolicyEvaluator;
    
    if ( iTimer )
        {
        iTimer->Cancel();
        delete iTimer;
        }
        
    delete iNodeIdGenerator;
    
    if ( iStack )
        {
        while ( !iStack->IsEmpty() )
            {
            MTransaction* tr = iStack->Pop();
            iFactory->ReleaseTransaction( tr );
            }

        delete iStack;
        }
    
    delete iFactory;
    
    iRefreshableUiElements.ResetAndDestroy();
    
    delete iNTClass;
    
    delete iPropertyMap;
    
    delete iContentPriorityMap;
    delete iCallbackHandler;
    }

void CContentRenderer::SetEventHandler( MAiFwEventHandler& aFwEventHandler )
    {
    iFwEventHandler = &aFwEventHandler;
    }

void CContentRenderer::Reset()
    {
    iContentPriorityMap->Reset();
    iPropertyMap->Reset();
    }

void CContentRenderer::CleanPluginFromUi( MAiPropertyExtension& aPlugin )
    {
    // Create transaction to clean UI
    TInt txId = reinterpret_cast< TInt >( &aPlugin );
    
    TBool txCreated = ( StartTransaction( txId ) == KErrNone );
    
    // Clean plugin
    TRAPD( cleanError, CleanPluginFromUiL( aPlugin ) );
    
    // Commit transaction
    if ( txCreated )
        {
        if (cleanError )
            {
            CancelTransaction( txId );
            }
        else
            {
            Commit( txId );
            }
        }
    }

TInt CContentRenderer::StartTransaction( TInt aTxId )
    {
    __PRINT(__DBG_FORMAT("\t[I]\tXML UI: Start transaction id=%d"), aTxId);
    __HEAP("XML UI: Start transaction");
    __TICK("XML UI: Start transaction");
    TRAPD( error, DoStartTransactionL( aTxId ) );

    if ( error )
        {
        SetImmediateMode( ETrue );
        }

    return error;
    }

TInt CContentRenderer::Commit( TInt aTxId )
    {
    // Remove transaction with aTxId from stack
    MTransaction* tr = iStack->Remove( aTxId );

    if ( tr )
        {
        // Commit transaction        
        RPropertyHashMap propertyHashMap;
        
        TBool layoutChanged( EFalse );
        
        TRAPD( error, tr->CommitL( layoutChanged, propertyHashMap ) );
        if( error == KErrNone )
            {
            TRAP_IGNORE( SetPropertyArraysL( propertyHashMap ) );
            }
        propertyHashMap.Close();

        __TICK("XML UI: Commit transaction");
        __HEAP("XML UI: Commit transaction");
        __PRINT(__DBG_FORMAT("\t[I]\tXML UI: Commit transaction id=%d"), aTxId);
       
        StartContentRefresh();
        
        TRAP_IGNORE
            (
            iAppUi.UiEngineL()->RenderUIL();                                  //jtm+++
            ProcessContentChangesL( *tr );
            );
                
        
        iFactory->ReleaseTransaction( tr );  
        
        return error;
        }
        
    return KErrNotSupported;
    }

void CContentRenderer::ProcessContentChangesL( MTransaction& aTr )
    {
    TDblQueIter<CTransactionElement> iter = aTr.ElementIter();
    RAiPolicyElementArray contentChangedArray;
    CleanupClosePushL( contentChangedArray );

    while ( iter )
          {
          CTransactionElement* element = iter++;
          CXnNodeAppIf& target = element->Target();
          
          // Find own and parents policy/ContentChanged nodes 
          iPolicyEvaluator->EvaluateContentChangedPolicyL( target, 
                  contentChangedArray);

          iPolicyEvaluator->EvaluateContentChangedPolicyL( *(target.ParentL()), 
                  contentChangedArray);
          }
       
    ::RemoveDuplicateContentChangesL( contentChangedArray );
        
    for ( TInt i = 0; i < contentChangedArray.Count(); ++i )
        {
        ProcessContentChangeL( contentChangedArray[i] );
        }
    CleanupStack::PopAndDestroy();
    }

void CContentRenderer::ProcessContentChangeL( TAiPolicyElement& aElement )
    {
    const TDesC8* id = PropertyValue( aElement.Target(), 
                                AiUiDef::xml::property::KId );
    if ( id )
        {
        CXnNodeAppIf* targetNode = FindNodeByIdL( *id, aElement.Target().Namespace() );
        if ( targetNode )
            {                     
            if ( aElement.Policy() == AiUiDef::xml::value::KShowTooltips )
                {
                targetNode->ShowTooltipsL();
                }                   
            }        
        }
    }

TInt CContentRenderer::CancelTransaction( TInt aTxId )
    {
    // Remove transaction with aTxId from stack
    MTransaction* tr = iStack->Remove( aTxId );

    if ( tr )
        {
        // Release transaction to factory
        __TICK("XML UI: Cancel transaction");
        __HEAP("XML UI: Cancel transaction");
        __PRINT(__DBG_FORMAT("\t[I]\tXML UI: Cancel transaction id=%d"), aTxId);

        iFactory->ReleaseTransaction( tr );

        return KErrNone;
        }

    return KErrNotSupported;
    }

TBool CContentRenderer::CanPublish( MAiPropertyExtension& aPlugin,
                                    TInt aContent,
                                    TInt aIndex )
    {
    TInt error( KErrNone );
    TInt retval( KErrNone );
    __PRINTS("*** UC: Init - Content Validation ***");
	__TIME("UC: Content Validation",
        TRAP( error, retval = CanPublishL( aPlugin, aContent, aIndex ) );
        );
    __HEAP("UC: Content Validation");
    __PRINTS("*** UC: Done - Content Validation ***");

    return ( error == KErrNone && retval == KErrNone );
    }

TInt CContentRenderer::Publish( MAiPropertyExtension& aPlugin,
                                TInt aContent,
                                TInt aResource,
                                TInt aIndex )
    {
	TInt error( KErrNone );
	TInt retval( KErrNone );
	__PRINTS("*** UC: Init - Content Publishing (Resource) ***");
	__TIME("UC: Content Publishing (Resource)",
    	TRAP( error, retval = DoPublishL( aPlugin, aContent, aResource, aIndex ) );
    );
    __HEAP("UC: Content Publishing (Resource)");
    __PRINTS("*** UC: Done - Content Publishing (Resource) ***");

    if( !error && retval )
        {
        error = retval;
        }
    
    return error;
    }

TInt CContentRenderer::Publish( MAiPropertyExtension& aPlugin,
                                TInt aContent,
                                const TDesC16& aText,
                                TInt aIndex )
    {
    TInt error( KErrNone );
    TInt retval( KErrNone );
    
    __PRINTS("*** UC: Init - Content Publishing (Value-Text) ***");
   	__TIME("UC: Content Publishing (Value-Text)",
    	TRAP( error, retval = DoPublishL( aPlugin, aContent, aText, aIndex ) );
    );
    __HEAP("UC: Content Publishing (Value-Text)");
    __PRINTS("*** UC: Done - Content Publishing (Value-Text) ***");

    if( !error && retval )
        {
        error = retval;
        }

    return error;
    }

TInt CContentRenderer::Publish( MAiPropertyExtension& aPlugin,
                                TInt aContent,
                                const TDesC8& aBuf,
                                TInt aIndex )
    {
    TInt error( KErrNone );
    TInt retval( KErrNone );
    
    __PRINTS("*** UC: Init - Content Publishing (Value-Buf) ***");
    __TIME("UC: Content Publishing (Value-Buf)",
    	TRAP( error, retval = DoPublishL( aPlugin, aContent, aBuf, aIndex ) );
    )
    __HEAP("UC: Content Publishing (Value-Buf)");
    __PRINTS("*** UC: Done - Content Publishing (Value-Buf) ***");

    if( !error && retval )
        {
        error = retval;
        }
    
    return error;
    }

TInt CContentRenderer::Publish( MAiPropertyExtension& aPlugin,
                                TInt aContent,
                                RFile& aFile,
                                TInt aIndex )
    {
    TInt error( KErrNone );
    TInt retval( KErrNone );
    	
	__PRINTS("*** UC: Init - Content Publishing (Value-RFile) ***");
    __TIME("UC: Content Publishing (Value-RFile)",
    	TRAP( error, retval = DoPublishL( aPlugin, aContent, aFile, aIndex ) );
    );
    __HEAP("UC: Content Publishing (Value-RFile)");
    __PRINTS("*** UC: Done - Content Publishing (Value-RFile) ***");

    if( !error && retval )
        {
        error = retval;
        }
    
    return error;
    }

TInt CContentRenderer::Clean( MAiPropertyExtension& aPlugin, TInt aContent, TInt aIndex )
    {
    TInt error( KErrNone );
    TInt retval( KErrNone );
            
    __PRINTS("*** UC: Init - Content Publishing (Clean) ***");
    __TIME("UC: Content Publishing (Clean)",
    	TRAP( error, retval = DoCleanL( aPlugin, aContent, aIndex ) );
    );
    __HEAP("UC: Content Publishing (Clean)");
    __PRINTS("*** UC: Done - Content Publishing (Clean) ***");

    if( !error && retval )
        {
        error = retval;
        }
    
    return error;
    }

TAny* CContentRenderer::Extension( TUid /*aUid*/ )
    {
    // No extensions supported
    return NULL;
    }

TBool CContentRenderer::RequiresSubscription( 
    const TAiPublisherInfo& aPublisherInfo ) const
    {
    if ( aPublisherInfo.iNamespace == KNativeUiNamespace )
        {
        // Not targeted to this content renderer
        return EFalse;
        }
        
    return ETrue;
    }

void CContentRenderer::DoStartTransactionL( TInt aTxId )
    {
    MTransaction* tr = iFactory->CreateTransactionL( aTxId );
    iStack->Push(tr);
    SetImmediateMode(EFalse);
    }

TInt CContentRenderer::CanPublishL( MAiPropertyExtension& aPlugin,
                                    TInt aContent,
                                    TInt aIndex )
    {
    // Get content item for aContent
    MAiContentItemIterator& iter( 
            ContentItemIteratorL( aPlugin, EAiPublisherContent ) );
    
    const TAiContentItem& item( iter.ItemL( aContent ) );

    // Lookup ui element
    const TDesC& nodeId( iNodeIdGenerator->ContentNodeIdL( aPlugin, item ) );

    CXnNodeAppIf* property( FindNodeByClassL( nodeId, aIndex, 
                            aPlugin.PublisherInfoL()->iNamespace ) ); 
 
    if( !property )
        {
        return KErrNotFound;
        }
    
    // Check content priority
    TInt priority( GetContentPriority( *property ) );

    CXnNodeAppIf* target( property->ParentL() );
    
    if( !target )
        {
        return KErrNotSupported;
        }

    if ( !AllowPublishByPriority( *target, priority ) )
        {
        return KErrAccessDenied;        
        }

    // Check if content type is supported by target
    const TDesC8& contentType( ContentType( item ) );

    if ( !iFactory->IsSupported( *target, contentType ) &&
          target->Type()->Type() != XnPropertyNames::listquerydialog::KListQueryDialog )
        {
        return KErrNotSupported;
        }
    
    return KErrNone;
    }

TInt CContentRenderer::DoPublishL( MAiPropertyExtension& aPlugin,
                                   TInt aContent,
                                   TInt aResource,
                                   TInt aIndex )
    {
    TAiPublisherInfo *info = static_cast<TAiPublisherInfo*>(aPlugin.GetPropertyL( EAiPublisherInfo ));
    if (!info)
        {
        return KErrNotFound;
        }
    // Read ref value.
    MAiContentItemIterator& refIter( ContentItemIteratorL( aPlugin, EAiPublisherResources ) );
    const TAiContentItem& ref( refIter.ItemL( aResource ) );

    const TDesC8& refType( ContentType( ref ) );

    // Resolve source node
    const TDesC& nodeId = iNodeIdGenerator->ResourceNodeIdL(aPlugin, ref);
    HBufC8* nodeId8 = CnvUtfConverter::ConvertFromUnicodeToUtf8L(nodeId);
    CleanupStack::PushL(nodeId8);
    CXnNodeAppIf* source = NULL;
    __TIME_MARK(xmlOverhead);
    source = FindNodeByIdL( *nodeId8, info->iNamespace );
    __TIME_ENDMARK("XML UI: Lookup node by id", xmlOverhead);
    __PRINT(__DBG_FORMAT("\t[I]\tXML UI: Lookup node by id=%S"), &nodeId);
    
    CleanupStack::PopAndDestroy(nodeId8);
    
    // Fetch content id
    MAiContentItemIterator& iter = ContentItemIteratorL( aPlugin, EAiPublisherContent );
    const TAiContentItem& item = iter.ItemL( aContent );

    const TDesC& targetId = iNodeIdGenerator->ContentNodeIdL(aPlugin, item);

    TInt retval( KErrNotSupported );

    // Check types
    if ( refType == KContentTypeText )
        {
        // Fetch text
        const TDesC8& text = source->GetPCData();
        
        // Delegate to data publishing function
        retval = PublishDataL( aPlugin,
                               targetId,
                               text,
                               refType,
                               aIndex,
                               source );
        }
    else if ( refType.Find( KContentTypeImage ) != KErrNotFound )
        {
        // Fetch icon
        CGulIcon* icon = LoadIconLC( *source ); 
        
       // Delegate to icon publishing function
        retval = PublishIconL( aPlugin,
                               targetId,
                               icon,
                               aIndex,
                               source );
        
        CleanupStack::Pop( icon );
        }
    
    return retval;
    }

TInt CContentRenderer::DoPublishL( MAiPropertyExtension& aPlugin,
                                   TInt aContent,
                                   const TDesC16& aText,
                                   TInt aIndex )
    {
    // Resolve content item
    MAiContentItemIterator& iter( 
            ContentItemIteratorL( aPlugin, EAiPublisherContent ) );
    
    const TAiContentItem& item( iter.ItemL( aContent ) );

    const TDesC8& type( ContentType( item ) );

    if ( type == KContentTypeText )
        {
        // Find ui element
        const TDesC& nodeId = iNodeIdGenerator->ContentNodeIdL( aPlugin, item );

        __TIME_MARK(xmlOverhead);

        CXnNodeAppIf* property( FindNodeByClassL( nodeId, aIndex, 
                                                  aPlugin.PublisherInfoL()->iNamespace ) );

        if( !property )
            {
            return KErrNotFound;
            }
        
        TInt priority( GetContentPriority( *property ) );
        
        __TIME_ENDMARK("XML UI: Lookup node by class", xmlOverhead);
    	__PRINT(__DBG_FORMAT("\t[I]\tXML UI: Lookup node by class=%S"), &nodeId);

        //Navigate to parent
        CXnNodeAppIf* target( property->ParentL() );

        if( !target )
            {
            return KErrNotSupported;
            }
        
        // Check priority
        if ( AllowPublishByPriority( *target, priority ) )
            {
            // Check if target is newsticker
            MTransactionElement* element( NULL );
            
            if ( IsParentNewsticker( *target ) )
                {
                // Register callback interface for newsticker
                CXnNodeAppIf *parent( target->ParentL() );
                
                if( !parent )
                    {
                    return KErrNotFound;
                    }
                                
                RegisterNewstickerCallbackInterfaceL( *parent );
                
                const TAiPublisherInfo* info( aPlugin.PublisherInfoL() );
                
                if ( info )
                    {
                    iNTPublisher.Set( info->iName );
                    iNTClass = AiUtility::CopyToBufferL( iNTClass, nodeId );
                    }
                
                element = iFactory->CreateNewsTickerTransactionElementL( *target,
                                                                    aText,
                                                                    priority, 
                                                                    aIndex );
                }
            else if( target->Type()->Type() == 
                     XnListQueryDialogInterface::MXnListQueryDialogInterface::Type())
                {
                // Get the data interface for dialog and publish data
                XnListQueryDialogInterface::MXnListQueryDialogInterface* listQuery( NULL );
                XnComponentInterface::MakeInterfaceL( listQuery, *target );
                LeaveIfNull( listQuery, KErrNotSupported );
                listQuery->ReplaceItemL( aText, aIndex -1 );// plugins publish ordinals not indexes
                return KErrNone;
                }
            else
                {
                // Create transaction element for text
                // Not put to cleanupstack, because element is from our pool!
                element = iFactory->CreateTextTransactionElementL( *target,
                                                                    aText,
                                                                    priority );
                }
            
            iPolicyEvaluator->EvaluateContentPolicyL( *target,
                                                      element->PolicyArray() );
            iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                         element->PolicyArray() );

            ProcessTransactionElementL( element );
            }
        else
            {
            return KErrAccessDenied;            
            }
        }
    else
        {
        return KErrNotSupported;
        }
    
    return KErrNone;
    }

TInt CContentRenderer::DoPublishL( MAiPropertyExtension& aPlugin,
                                   TInt aContent,
                                   const TDesC8& aBuf,
                                   TInt aIndex )
    {
    // resolve content item
    MAiContentItemIterator& iter( 
            ContentItemIteratorL( aPlugin, EAiPublisherContent ) );
    
    const TAiContentItem& item( iter.ItemL( aContent ) );

    const TDesC8& type( ContentType( item ) );

    TInt retval( KErrNotSupported );
    
    if( type == KContentTypeBitmap )
        {
        // Unpack icon from pointer
        CGulIcon* icon( LeaveIfNull( UnpackPtr<CGulIcon>( aBuf ), KErrArgument ) );

        const TDesC& nodeId( iNodeIdGenerator->ContentNodeIdL( aPlugin, item ) );
        
        // Publish icon
        retval = PublishIconL( aPlugin, nodeId, icon, aIndex );
        }
    else if ( type == KContentTypeImageSvg )
        {
        // Get node id
        const TDesC& nodeId( iNodeIdGenerator->ContentNodeIdL( aPlugin, item ) );
        
        // Publish SVG data
        retval = PublishDataL( aPlugin, nodeId, aBuf, KContentTypeImageSvg, aIndex );
        }

    return retval;
    }

TInt CContentRenderer::DoPublishL( MAiPropertyExtension& aPlugin,
                                   TInt aContent,
                                   RFile& aFile,
                                   TInt aIndex)
    {
    //Resolve content item
    MAiContentItemIterator& iter( 
            ContentItemIteratorL( aPlugin, EAiPublisherContent ) );
    
    const TAiContentItem& item( iter.ItemL( aContent ) );

    const TDesC8& type( ContentType( item ) );

    // Image support
    if ( type.Find( KContentTypeImage ) != KErrNotFound )
        {
        // Find ui element
        const TDesC& nodeId( iNodeIdGenerator->ContentNodeIdL( aPlugin, item ) );

        CXnNodeAppIf* property( FindNodeByClassL( nodeId, aIndex, 
                                aPlugin.PublisherInfoL()->iNamespace ) );
        
        if( !property )
            {
            return KErrNotFound;
            }
        
        // Check priority
        TInt priority( GetContentPriority( *property ) );
        
        CXnNodeAppIf* target( property->ParentL() );
        
        if( !target )
            {
            return KErrNotSupported;
            }

        if ( AllowPublishByPriority( *target, priority ) )
            {
            // Check if target is newsticker
            if ( IsParentNewsticker( *target ) )
                {
                // Register callback interface
                CXnNodeAppIf *parent( target->ParentL() );
                
                if( !parent )
                    {
                    return KErrNotFound;
                    }
                                
                RegisterNewstickerCallbackInterfaceL( *parent );
                
                const TAiPublisherInfo* info( aPlugin.PublisherInfoL() );
                
                if( info )
                    {
                    iNTPublisher.Set( info->iName );
                    iNTClass = AiUtility::CopyToBufferL( iNTClass, nodeId );
                    }
                }
            
            // Create transaction element for file
            MTransactionElement* element =
                iFactory->CreateImageTransactionElementL( *target,
                                                          aFile,
                                                          priority );
            
            iPolicyEvaluator->EvaluateContentPolicyL( *target,
                                                      element->PolicyArray() );
            iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                         element->PolicyArray() );
            
            ProcessTransactionElementL( element );
            }
        else
            {
            return KErrAccessDenied;
            }
        }
    else 
        {
        return KErrNotSupported;        
        }
    
    return KErrNone;
    }

TInt CContentRenderer::DoCleanL( MAiPropertyExtension& aPlugin,
                                 TInt aContent,
                                 TInt aIndex )
    {
    // Resolve content item
    MAiContentItemIterator& iter( 
            ContentItemIteratorL( aPlugin, EAiPublisherContent ) );
    
    const TAiContentItem& item( iter.ItemL( aContent ) );

    const TDesC& nodeId( iNodeIdGenerator->ContentNodeIdL( aPlugin, item ) );

    CXnNodeAppIf* property( FindNodeByClassL( nodeId, aIndex, 
                            aPlugin.PublisherInfoL()->iNamespace ) );

    if( !property )
        {
        return KErrNotFound;
        }
    
    TInt priority( GetContentPriority( *property ) );

    // Navigate to parent
    CXnNodeAppIf* target( property->ParentL() );

    if( !target )
        {
        return KErrNotSupported;
        }
    
    if ( !AllowPublishByPriority( *target, priority ) )
        {
        return KErrAccessDenied;
        }

    if ( IsParentNewsticker( *target ) )
        {
        CXnNodeAppIf *parent( target->ParentL() );
        
        if( !parent )
            {
            return KErrNotFound;
            }
               
        RegisterNewstickerCallbackInterfaceL( *parent );
        
        const TAiPublisherInfo* info( aPlugin.PublisherInfoL() );
        
        if( info )
            {
            iNTPublisher.Set( info->iName );
            iNTClass = AiUtility::CopyToBufferL( iNTClass, nodeId );
            }
        }
    if( target->Type()->Type() ==
        XnListQueryDialogInterface::MXnListQueryDialogInterface::Type())
        {
        // Get the data interface for dialog and delete data
        XnListQueryDialogInterface::MXnListQueryDialogInterface* listQuery( NULL );
        XnComponentInterface::MakeInterfaceL( listQuery, *target );
        LeaveIfNull( listQuery, KErrNotSupported );
        listQuery->DeleteItem( aIndex -1 );// plugins publish ordinals not indexes
        return KErrNone;
        }
    
    // Create transaction element for empty content
    MTransactionElement* element =
        iFactory->CreateEmptyContentTransactionElementL( *target, aIndex );

    iPolicyEvaluator->EvaluateEmptyContentPolicyL( *target,
                                                   element->PolicyArray() );
    iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                   element->PolicyArray() );

    ProcessTransactionElementL( element );
      
    if ( priority > KErrNotFound ) // Only for prioritized elements
        {
        // Add current ui element into content refresh map
        HBufC* uiElementId = PropertyValueL( *target,
                                         XnPropertyNames::common::KId );
        return RefreshContentL( uiElementId, priority );
        }
    
    return KErrNone;
    }

void CContentRenderer::SetImmediateMode( TBool aImmediateMode )
    {
    iImmediateMode = aImmediateMode;
    }

TBool CContentRenderer::IsImmediateMode() const
    {
    return iImmediateMode;
    }

void CContentRenderer::ProcessTransactionElementL( MTransactionElement* aElement )
    {
    LeaveIfNull( aElement, KErrArgument );
    
    if ( IsImmediateMode() || iStack->IsEmpty() )
        {
        // No transaction. Commit element immediately
        TBool layoutChanged = EFalse;
        
        RPropertyHashMap propertyHashMap;
        aElement->CommitL(layoutChanged, propertyHashMap);
        SetPropertyArraysL( propertyHashMap );
        propertyHashMap.Close();
        
        iFactory->ReleaseTransactionElement( aElement );
        StartContentRefresh();
        
        // Re-layout
        iAppUi.UiEngineL()->RenderUIL();
        }
    else
        {
        // Append transaction element to transaction
        MTransaction* tr = iStack->Top();
        tr->Append( *aElement );
        }
    }

CXnNodeAppIf* CContentRenderer::FindNodeByClassL( const TDesC& aCid,
                                                  TInt aIndex,
                                                  const TDesC8& aNs )
    {
    // Find node
    HBufC8* classId = CnvUtfConverter::ConvertFromUnicodeToUtf8L(aCid);
    CleanupStack::PushL(classId);
    
    RPointerArray<CXnNodeAppIf> nodes = iAppUi.UiEngineL()->FindNodeByClassL( *classId, aNs );

    CleanupStack::PopAndDestroy(classId);

    CleanupClosePushL( nodes );

    for ( TInt i = 0; i < nodes.Count(); ++i )
        {
        CXnNodeAppIf* node = nodes[i];

        const TDesC8* name =
            PropertyValue( *node, AiUiDef::xml::property::KName );

        if ( name && ( *name == AiUiDef::xml::name::KOrdinal ) )
            {
            const TDesC8* value =
                PropertyValue( *node, AiUiDef::xml::property::KValue );

            if ( value )
                {
                // Try to parse index from string either

                TInt32 index( 0 );
                User::LeaveIfError( AiUtility::ParseInt( index, *value ) );

                if ( index == aIndex )
                    {
                    CleanupStack::PopAndDestroy(&nodes);
                    return node;
                    }
                }
            }
        else if ( name && ( *name == AiUiDef::xml::name::KTarget ) )
            {            
            const TDesC8* target =
                           PropertyValue( *node, AiUiDef::xml::property::KValue );
            
            CXnNodeAppIf* targetNode = FindNodeByIdL( *target, node->Namespace() );
            if ( targetNode )
                {
                CleanupStack::PopAndDestroy( &nodes );
                return targetNode;
                }
            }
        else if ( nodes.Count() == 1 ) // Only one node in class
            {
            // No ordinal specified
			node = nodes[ 0 ];
			CleanupStack::PopAndDestroy(&nodes);
            return node;
            }
        }
    
    CleanupStack::PopAndDestroy( &nodes );
    
    return NULL; // Never reached. Needed to omit compiler warning
    }

CXnNodeAppIf* CContentRenderer::FindNodeByIdL( const TDesC& aCid, const TDesC& aNs )
    {
    // Find node
    return LeaveIfNull( iAppUi.UiEngineL()->FindNodeByIdL( aCid, aNs ),
                        KErrNotFound );
    }

CXnNodeAppIf* CContentRenderer::FindNodeByIdL( const TDesC8& aCid, const TDesC8& aNs )
    {
    // Find node
    return LeaveIfNull( iAppUi.UiEngineL()->FindNodeByIdL( aCid, aNs ),
                        KErrNotFound );
    }

TInt CContentRenderer::PublishIconL( MAiPropertyExtension& aPlugin,
                                     const TDesC& aCid,
                                     CGulIcon* aIcon,
                                     TInt aIndex,
                                     CXnNodeAppIf* aResource )
    {
    // Find proiperty element by class
    CXnNodeAppIf* property( FindNodeByClassL( aCid, aIndex, 
                            aPlugin.PublisherInfoL()->iNamespace ) );
    
    if( !property )
        {
        return KErrNotFound;
        }
    
    // Get priority information
    TInt priority( GetContentPriority( *property ) );
    
    // Navigate to parent
    CXnNodeAppIf* target( property->ParentL() );
    
    if( !target )
        {
        return KErrNotFound;
        }

    // Check priority
    if ( !AllowPublishByPriority( *target, priority ) )
        {
        return KErrAccessDenied;        
        }
    
    // Special handling of newsticker
    if ( IsParentNewsticker( *target ) )
        {
        // Register callback interface
        CXnNodeAppIf *parent( target->ParentL() );
        
        if( !parent )
            {
            return KErrNotFound;
            }
                
        RegisterNewstickerCallbackInterfaceL( *parent );
        
        const TAiPublisherInfo* info( aPlugin.PublisherInfoL() );
        
        if( info )
            {
            iNTPublisher.Set( info->iName );
            iNTClass = AiUtility::CopyToBufferL( iNTClass, aCid );
            }
        }

    MTransactionElement* element =
        iFactory->CreateImageTransactionElementL( *target,
                                                  aIcon,
                                                  priority );

    if ( aResource )
        {
        iPolicyEvaluator->EvaluateResourcePolicyL( *target,
                                                   *aResource,
                                                   element->PolicyArray() );
        iPolicyEvaluator->EvaluateContentPolicyL( *target,
                                                  element->PolicyArray() );
        iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                     element->PolicyArray() );
        }

    else
        {
        iPolicyEvaluator->EvaluateContentPolicyL( *target,
                                                  element->PolicyArray() );
        iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                     element->PolicyArray() );
        }

    ProcessTransactionElementL( element );
    
    return KErrNone;
    }

TInt CContentRenderer::PublishDataL( MAiPropertyExtension& aPlugin,
                                     const TDesC& aCid,
                                     const TDesC8& aData,
                                     const TDesC8& aContentType,
                                     TInt aIndex,
                                     CXnNodeAppIf* aResource )
    {
    CXnNodeAppIf* property( FindNodeByClassL( aCid, aIndex, 
                            aPlugin.PublisherInfoL()->iNamespace ) );

    if( !property )
        {
        return KErrNotFound;
        }
    
    TInt priority( GetContentPriority( *property ) );
    
    // Navigate to parent
    CXnNodeAppIf* target( property->ParentL() );
    
    if( !target )
        {
        return KErrNotFound;
        }

    if ( !AllowPublishByPriority( *target, priority ) )
        {
        return KErrAccessDenied;        
        }

    if ( !CDataBufferTransactionElement::IsSupported( *target, aContentType ) )
        {
        return KErrNotSupported;        
        }
        
    // Handle newsticker 
    if ( IsParentNewsticker( *target ) )
        {
        CXnNodeAppIf *parent( target->ParentL() );
        
        if( !parent )
            {
            return KErrNotFound;
            }
                
        RegisterNewstickerCallbackInterfaceL( *parent );
        
        const TAiPublisherInfo* info( aPlugin.PublisherInfoL() );
        
        if( info )
            {
            iNTPublisher.Set( info->iName );
            iNTClass = AiUtility::CopyToBufferL( iNTClass, aCid );
            }
        }
        
    MTransactionElement* element =
        iFactory->CreateDataBufferTransactionElementL( *target,
                                                       aData,
                                                       priority );
    
    if ( aResource )
        {
        iPolicyEvaluator->EvaluateResourcePolicyL( *target,
                                                   *aResource,
                                                   element->PolicyArray() );
        iPolicyEvaluator->EvaluateContentPolicyL( *target,
                                                  element->PolicyArray() );
        iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                     element->PolicyArray() );
        }
    else
        {
        iPolicyEvaluator->EvaluateContentPolicyL( *target,
                                                  element->PolicyArray() );
        iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                     element->PolicyArray() );
        }

    ProcessTransactionElementL( element );
    
    return KErrNone;
    }

TBool CContentRenderer::AllowPublishByPriority( CXnNodeAppIf& aUiElement,
                                                TInt aPriority ) const
    {
    // Get ui element id
    const TDesC8* uiElementId = PropertyValue( aUiElement,
                                               XnPropertyNames::common::KId );
                                         
    if ( uiElementId )
        {
        // compare given priority with the current value of ui element
        return iContentPriorityMap->OverrideContent( *uiElementId, aPriority );
        }
    
    return EFalse; // priority cannot be used, because ui element does not have id
    }

void CContentRenderer::StartContentRefresh()
    {
    // Cancel ongoing refresh
    iTimer->Cancel();
    
    if ( iRefreshableUiElements.Count() > 0 )
        {
        // Refreshable elements exist. Start timer to make refresh asynchronous 
        iTimer->Start( TTimeIntervalMicroSeconds32( 0 ),
                       TTimeIntervalMicroSeconds32( 0 ),
                       TCallBack( RefreshContentCallback, this ) );
        }
    }

/**
 * Adds ui element to list of refreshable elements
 *
 * @param aUiElementId ui element id
 * @param aOldPriority old priority value
 */                         
TInt CContentRenderer::RefreshContentL( HBufC* aUiElementId,
                                        TInt aOldPriority )
    {
    if( !aUiElementId )
        {
        return KErrArgument;
        }
        
    // Take ownership of aUiElementId
    CleanupStack::PushL( aUiElementId );
    
    if ( !iFwEventHandler )
        {
        // Content refresh event cannot be sent
        CleanupStack::PopAndDestroy( aUiElementId );
        return KErrNotReady;
        }
    
    // Find current mapping
    TInt* oldPriority = iRefreshableUiElements.Find( *aUiElementId );
    
    if ( oldPriority )
        {
        // Update mapping
        *oldPriority = aOldPriority;
        CleanupStack::PopAndDestroy( aUiElementId );
        }
    else
        {
        // Create new mapping
        oldPriority = new( ELeave ) TInt( aOldPriority );
        CleanupStack::PushL( oldPriority );
    
        User::LeaveIfError( iRefreshableUiElements.Insert( aUiElementId,
                                                           oldPriority ) );
                                                       
        CleanupStack::Pop( 2, aUiElementId );
        }
    
    return KErrNone;
    }

/**
 * Callback function to make content refreshing asynchronous
 */    
TInt CContentRenderer::RefreshContentCallback( TAny* aContentRenderer )
    {
    if ( !aContentRenderer )
        {
        return KErrArgument;
        }
        
    CContentRenderer* renderer = static_cast< CContentRenderer* >( aContentRenderer );
    TRAP_IGNORE( renderer->SendRefreshContentEventL() );
    return KErrNone;
    }

/**
 * Sends Refresh content event to framework.
 * Event is sent for content selectors with lower priority than the
 * last content which has been cleaned from ui element.
 */ 
void CContentRenderer::SendRefreshContentEventL()
    {
    // Cancel periodic timer.
    iTimer->Cancel();
    
    // Get ui element id and old content priority
    TPtrHashMapIter< TDesC, TInt> iter( iRefreshableUiElements );
    iter.Reset();
    
    const TDesC* uiElementId = iter.NextKey(); // Never NULL
    TInt priority = *( iter.CurrentValue() );
    
    //  Cleanup item for iterator 
    TMapCleanupItem cleanup( iter );
    CleanupReleasePushL( cleanup );
    
    // Lookup ui element
    CXnNodeAppIf* uiElement = FindNodeByIdL( *uiElementId );
    
    // Remove current ui element from the map
    CleanupStack::PopAndDestroy( &cleanup );
    
    // Find lower priority content elements associated to this ui element
    RPointerArray< CXnNodeAppIf > children = uiElement->ChildrenL();
    
    // Remove higher priority content elements
    RemoveNonPriorityElements( children, priority );
        
    // Sort array to descending order
    children.Sort( TLinearOrder< CXnNodeAppIf >( DescendingPriorityOrder ) );
    
    // Send event for content selectors in descending priority order.
    // Continue until first content gets published or array exhausts.
    for ( TInt i = 0; i < children.Count(); ++i )
        {
        CXnNodeAppIf* current = children[ i ];
        
        // Get content selector
        const HBufC* contentSelector = PropertyValueL( *current,
                                                       XnPropertyNames::common::KClass );
        
        if ( contentSelector && iFwEventHandler->RefreshContent( *contentSelector ))
            {
            break;
            }
        }
    
    // Free content selector array
    children.Reset();
    
    // Continue content refresh for next ui element.
    StartContentRefresh();    
    }

void CContentRenderer::TitleScrolled( TInt aTitleIndex )
    {
    if ( iCallbackHandler )
        {
        TRAP_IGNORE( iCallbackHandler->TitleScrolledL( iNTPublisher,
                                                       *iNTClass,
                                                       aTitleIndex ) );
        }
    }
    
void CContentRenderer::TitleToScroll( TInt aTitleIndex )
    {
    if ( iCallbackHandler )
        {
        TRAP_IGNORE( iCallbackHandler->TitleToScrollL( iNTPublisher,
                                                       *iNTClass,
                                                       aTitleIndex ) );        
        }
    }

TBool CContentRenderer::IsParentNewsticker( CXnNodeAppIf& aTarget )
    {
    CXnNodeAppIf* parent = NULL;
    TRAP_IGNORE( parent = aTarget.ParentL() );
    if ( !parent )
        {
        return EFalse;
        }
    CXnType* typeInfo = parent->Type();
    
    if ( !typeInfo )
        {
        return EFalse;
        }
        
    return ( typeInfo->Type() == XnNewstickerInterface::MXnNewstickerInterface::Type());
    }
    
void CContentRenderer::RegisterNewstickerCallbackInterfaceL( CXnNodeAppIf& aTarget )
    {
    if ( !iCallbackHandler )
        {
        // Instantiate callback handler
        CNewstickerCallbackHandler* handler = CNewstickerCallbackHandler::NewLC( *iFwEventHandler );

        // Set callback handler
        iCallbackHandler = handler;
        CleanupStack::Pop( handler );
        }
        // Obtain newsticker component interface
        XnNewstickerInterface::MXnNewstickerInterface* newsticker = NULL;
        XnComponentInterface::MakeInterfaceL( newsticker, aTarget );
        
        LeaveIfNull( newsticker, KErrGeneral );
        
        // Set callback interface
        newsticker->SetCallbackInterfaceL( this );
    }

void CContentRenderer::CleanPluginFromUiL( MAiPropertyExtension& aPlugin )
    {
    TInt itemCount = 0;
    
    // Resolve content items
    MAiContentItemIterator& iter = ContentItemIteratorL( aPlugin, EAiPublisherContent );
    iter.Reset();
    
    // Clean all content items
    while ( iter.HasNext() )
        {
        const TAiContentItem& item = iter.NextL();

        const TDesC& nodeId = iNodeIdGenerator->ContentNodeIdL( aPlugin, item );
        
        // Find nodes
        HBufC8* nodeId8 = CnvUtfConverter::ConvertFromUnicodeToUtf8L(nodeId);
        CleanupStack::PushL(nodeId8);

        RPointerArray<CXnNodeAppIf> nodes = iAppUi.UiEngineL()->FindNodeByClassL( 
            *nodeId8, aPlugin.PublisherInfoL()->iNamespace );

        CleanupStack::PopAndDestroy(nodeId8);

        itemCount += nodes.Count(); 
        
        CleanupClosePushL( nodes );

        for ( TInt i = 0; i < nodes.Count(); ++i )
            {
            CXnNodeAppIf* property = nodes[i];
            
            TInt priority = GetContentPriority( *property );

            // Navigate to parent
            CXnNodeAppIf* target = LeaveIfNull( property->ParentL(), KErrNotFound );

            if ( AllowPublishByPriority( *target, priority ) )
                {
                // Create transaction element for empty content
                MTransactionElement* element =
                    iFactory->CreateEmptyContentTransactionElementL( *target, 0 );

                iPolicyEvaluator->EvaluateEmptyContentPolicyL( *target,
                                                               element->PolicyArray() );
                iPolicyEvaluator->EvaluateVisibilityPolicyL( *target,
                                                               element->PolicyArray() );
                                                      
                ProcessTransactionElementL( element );
                  
                if ( priority > KErrNotFound ) // Only for prioritized elements
                    {
                    // Add current ui element into content refresh map
                    HBufC* uiElementId = PropertyValueL( *target,
                                                         XnPropertyNames::common::KId );
                    RefreshContentL( uiElementId, priority );
                    }
                }
            }
            
        CleanupStack::PopAndDestroy(&nodes);
        }
        
    if ( itemCount == 0 )
        {
        //Nothing to clean from UI. Cancel transaction outside current trap harness
        User::Leave( KErrNotFound );
        }
    }

//  End of File