mpx/collectionframework/collectionengine/src/mpxcollectionclientcontext.cpp
changeset 0 a2952bb97e68
child 15 d240f0a77280
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpx/collectionframework/collectionengine/src/mpxcollectionclientcontext.cpp	Thu Dec 17 08:55:47 2009 +0200
@@ -0,0 +1,2613 @@
+/*
+* Copyright (c) 2006 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:  Maintains sessions to paths
+*
+*/
+
+#include <badesca.h>
+#include <mpxcollectionpath.h>
+#include <mpxclientlist.h>
+#include <mpxcollectionplugin.h>
+#include <mpxcollectionpath.h>
+#include <mpxcollectionframeworkdefs.h>
+#include <mpxcollectioncommanddefs.h>
+#include <mpxcommonframeworkdefs.h>
+#include <mpxcmn.h>
+#include <mpxmedia.h>
+#include <mpxmediaarray.h>
+#include <mpxmediageneraldefs.h>
+#include <mpxmediacontainerdefs.h>
+#include <mpxmessagegeneraldefs.h>
+#include <mpxmessagecontainerdefs.h>
+#include <mpxcollectionmessage.h>
+#include <mpxcollectionmessagedefs.h>
+#include <mpxcommandgeneraldefs.h>
+#include <mpxcollectionopenlresultdef.h>
+#include <mpxcommand.h>
+#include <mpxlog.h>
+#include <mpxsubscription.h>
+#include "mpxcollectionengineobserver.h"
+#include "mpxcollectionpluginhandler.h"
+#include "mpxcollectionengine.h"
+#include "mpxcollectioncache.h"
+#include "mpxcollectionclientcontext.h"
+
+// ----------------------------------------------------------------------------
+// Helper. Sets next open mode on path
+// ----------------------------------------------------------------------------
+//
+LOCAL_C void SetPathOpenMode(CMPXCollectionPath& aPath, TInt aMode)
+    {
+    TMPXOpenMode mode=static_cast<TMPXOpenMode>(aMode);
+    if (aPath.Levels())
+        {
+        aPath.Set(mode == EMPXOpenDefault ? aPath.OpenPreviousMode() : mode);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Helper. Returns whether next open mode is the same as previous open
+// request
+// ----------------------------------------------------------------------------
+//
+LOCAL_C TBool OpenModeValid(const CMPXCollectionPath& aPath)
+    {
+    TMPXOpenMode next=aPath.OpenNextMode();
+    return (aPath.OpenPreviousMode() == next) || (next == EMPXOpenDefault);
+    }
+
+// ============================ MEMBER FUNCTIONS ==============================
+
+// ----------------------------------------------------------------------------
+// Two-phased constructor.
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionClientContext* CMPXCollectionClientContext::NewL(
+    CMPXCollectionEngine& aEngine,
+    CMPXCollectionCache& aCache,
+    const TUid& aModeId)
+    {
+    CMPXCollectionClientContext* p =
+                    new(ELeave) CMPXCollectionClientContext(aEngine, aCache, aModeId);
+    CleanupStack::PushL(p);
+    p->ConstructL();
+    CleanupStack::Pop(p);
+    return p;
+    }
+
+// ----------------------------------------------------------------------------
+// C++ constructor.
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionClientContext::CMPXCollectionClientContext(
+    CMPXCollectionEngine& aEngine,
+    CMPXCollectionCache& aCache,
+    const TUid& aModeId)
+  : iModeId(aModeId),iFocusItemId(KMPXInvalidItemId),
+    iEngine(aEngine),iCache(aCache)
+    {
+    }
+
+// ----------------------------------------------------------------------------
+// 2nd phase constructor.
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::ConstructL()
+    {
+    iClientList = CMPXClientList::NewL();
+    iBrowsePath = CMPXCollectionPath::NewL();
+    iMedia = CMPXMedia::NewL(); // empty media
+    // initialize to null uid
+    for (TInt index = 0; index < EContextCount; ++index)
+        {
+        iPluginUids.AppendL(KNullUid);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Destructor
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionClientContext::~CMPXCollectionClientContext()
+    {
+    MPX_DEBUG2("CMPXCollectionClientContext::~CMPXCollectionClientContext %08x",
+               this);
+
+    // Release all plugins this context has references on, this could result in the
+    // plugin(s) being unloaded.
+    for (TInt index = 0; index < EContextCount; ++index)
+        {
+        if (iPluginUids[index] != KNullUid)
+            {
+            iEngine.ReleasePlugin(iPluginUids[index]);
+            }
+        }
+
+    delete iClientList;
+    delete iBrowsePath;
+    delete iMediaPath;
+    delete iMedia;
+    delete iFilter;
+    iPluginUids.Close();
+    iUids.Close();
+    }
+
+// ----------------------------------------------------------------------------
+// Open collection by path
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::OpenL(
+    CMPXCollectionPath* aPath,
+    TInt aMode,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 1 <--");
+    MPX_ASSERT(aPath);
+    if (aPath->Levels() > 0)
+        {
+        // Increment the plugin reference count
+        CMPXCollectionPlugin*plugin = iEngine.ResolvePluginL(*aPath);
+        if (!plugin)
+            {
+            MPX_DEBUG1("CMPXCollectionClientContext::OpenL 1 bad path plugin");
+            User::Leave(KErrArgument);
+            }
+
+        iEngine.CleanupPluginPushL(plugin);
+
+        // Ownership of aPath transferred
+        MPX_DEBUG1("CMPXCollectionClientContext::OpenL 1 Add task");
+        plugin->AddTaskL(EMcsOpenPath, aCallback, this,
+                        aMode, NULL, plugin, aPath);
+
+        // Pop the plugin
+        iEngine.PluginPop();
+        }
+    else
+        { // Go back to root level
+        MPX_DEBUG1("CMPXCollectionClientContext::OpenL restarting path");
+        iIndex=0;
+        iFocusItemId = KMPXInvalidItemId;
+        InitL(aCallback);
+        }
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 1 -->");
+    }
+
+// ----------------------------------------------------------------------------
+// Open collection by index
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::OpenL(
+    TInt aIndex,
+    TInt aMode,
+    const TArray<TMPXAttribute>& aAttrs,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 2 <--");
+    if (iBrowsePath->Levels()<=0)
+        {
+        User::Leave(KErrNotReady);
+        }
+    if (aIndex<0 || aIndex>=iBrowsePath->Count())
+        {
+        User::Leave(KErrArgument);
+        }
+    iBrowsePath->Set(aIndex);
+    // plugin not resolved yet
+    if (iPluginUids[EContextBrowse] == KNullUid)
+        {
+        // Increment the new plugin reference count, decrement the old.
+        // No need to push/release the plugin it is stored in a member
+        // variable and will be released when that is overwritten.
+        ResolvePluginL(*iBrowsePath, iPluginUids[EContextBrowse]);
+        }
+
+    if (iPluginUids[EContextBrowse] == KNullUid)
+        {
+        MPX_DEBUG1("CMPXCollectionClientContext::OpenL 2 bad plugin");
+        User::Leave(KErrNotReady);
+        }
+
+    // add request to the queue
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 2 Add task");
+    iBrowsePath->SetL(aAttrs);
+    LoadedPlugin(EContextBrowse)->AddTaskL(EMcsOpenIndex, aCallback, this,
+                                           aIndex, NULL, (TAny*)aMode);
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 2 -->");
+    }
+
+// ----------------------------------------------------------------------------
+// Open collection by uids
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::OpenL(
+    const TArray<TUid>& aUids,
+    TInt aMode,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    // Update path for open request
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 3 <---");
+    SetPathOpenMode(*iBrowsePath,aMode);
+    if (!MPXUser::CompareOrderedUidArrays(iUids.Array(), aUids))
+        { // the same context
+        MPX_DEBUG1("CMPXCollectionClientContext::OpenL 3 return Open");
+        aCallback->HandleOpen(iMedia, iIndex, ETrue, iMediaType);
+        }
+    else
+        { // go back to root level
+        MPX_DEBUG1("CMPXCollectionClientContext::OpenL 3 Going to Root");
+        iUids.Reset();
+        ::CopyArrayL(aUids, iUids);
+        iIndex=0;
+        iFocusItemId = KMPXInvalidItemId;
+        InitL(aCallback);
+        }
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 3 --->");
+    }
+
+// ----------------------------------------------------------------------------
+// Request current content of browse context
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::OpenL(
+    TInt aMode,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    // Update path for open request
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 4 <---");
+    SetPathOpenMode(*iBrowsePath,aMode);
+    if (iPluginUids[EContextBrowse] == KNullUid)
+        {// No plug-in has been resolved
+        if (iBrowsePath->Levels() == 0)
+            //
+            // Open called at root level: just need to list the plug-ins,
+            // no need to resolve any plugin. Open request doesn't apply
+            // at this level
+            {
+            MPX_DEBUG1("CMPXCollectionClientContext::OpenL 4 InitL()");
+            iIndex=0;
+            iFocusItemId = KMPXInvalidItemId;
+            InitL(aCallback);
+            }
+        else if (OpenModeValid(*iBrowsePath) && !iPathUpdated)
+            //
+            // The existing media is still valid (no change
+            // in the request for entries
+            //
+            {
+            MPX_DEBUG1("CMPXCollectionClientContext::OpenL 4 return media");
+            aCallback->HandleOpen(iMedia,
+                                  iIndex, ETrue,
+                                  iMediaType);
+            }
+           else // Open mode has changed and open NOT called at root level
+               {
+               MPX_DEBUG1("CMPXCollectionClientContext::OpenL 4 add task different open mode");
+               ResolvePluginL(*iBrowsePath, iPluginUids[EContextBrowse]); // Find a plug-in
+               if (iPluginUids[EContextBrowse] != KNullUid)
+                {
+                LoadedPlugin(EContextBrowse)->AddTaskL(EMcsOpen, aCallback, this);
+                }
+               }
+        }
+    else
+        {
+        MPX_DEBUG1("CMPXCollectionClientContext::OpenL 4 add task");
+        LoadedPlugin(EContextBrowse)->AddTaskL(EMcsOpen, aCallback,this);
+        }
+    MPX_DEBUG1("CMPXCollectionClientContext::OpenL 4 --->");
+    }
+
+// ----------------------------------------------------------------------------
+// Media request by collection path
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::BackL(
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    MPX_DEBUG1("CMPXCollectionClientContext::BackL()");
+    if (iPluginUids[EContextBrowse]==KNullUid)
+        {
+        User::Leave(KErrNotReady);
+        }
+
+    MPX_DEBUG1("CMPXCollectionClientContext::BackL() -- Add task");
+    LoadedPlugin(EContextBrowse)->AddTaskL(EMcsBack, aCallback, this);
+    }
+
+// ----------------------------------------------------------------------------
+// MediaL command
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::MediaL(
+    const CMPXCommand& aCmd,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    CMPXCollectionPath* path =
+        aCmd.ValueCObjectL<CMPXCollectionPath>(KMPXCommandGeneralTargetIds);
+    CleanupStack::PushL(path);
+
+    // Increment the plugin reference count
+    CMPXCollectionPlugin*plugin = iEngine.ResolvePluginL(*path);
+    if (!plugin)
+        {
+        User::Leave(KErrArgument);
+        }
+
+    iEngine.CleanupPluginPushL(plugin);
+
+    CMPXCommand* cmd = CMPXCommand::NewL(aCmd); // make a copy
+    CleanupStack::PushL(cmd);
+
+    plugin->AddTaskL(EMcsMediaByPath, aCallback, this,
+                     0, NULL, plugin, cmd, path);
+
+    CleanupStack::Pop(cmd); // Ownership transferred to the task queue
+    iEngine.PluginPop();    // Pop the plugin
+    CleanupStack::Pop(path);// Ownership transferred to the task queue
+    }
+
+// ----------------------------------------------------------------------------
+// Add a media
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::AddL(const CMPXMedia& aMedia)
+    {
+    DoUpdateMediaL( EMcsAddItem, aMedia );
+    }
+
+// ----------------------------------------------------------------------------
+// Remove a media
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::RemoveL(const CMPXMedia& aMedia)
+    {
+    DoUpdateMediaL( EMcsRemoveItem, aMedia );
+    }
+
+// ----------------------------------------------------------------------------
+// Update a media
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::SetL(const CMPXMedia& aMedia)
+    {
+    DoUpdateMediaL( EMcsSetMedia, aMedia );
+    }
+
+// ----------------------------------------------------------------------------
+// Remove media by path
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::RemoveL(
+    CMPXCollectionPath* aPath,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    TInt id(aPath->Id(CMPXCollectionPath::ECollectionUid));
+
+    // Increment the plugin reference count
+    CMPXCollectionPlugin* plugin = iEngine.ResolvePluginL(TUid::Uid(id));
+    if (!plugin)
+        {
+        User::Leave(KErrArgument);
+        }
+
+    iEngine.CleanupPluginPushL(plugin);
+
+    // Ownership of aPath transferred
+    plugin->AddTaskL(EMcsRemovePath, aCallback, this, 0, NULL, plugin, aPath);
+
+    iEngine.PluginPop();
+    }
+
+// ----------------------------------------------------------------------------
+// Return current plugin id
+// ----------------------------------------------------------------------------
+//
+EXPORT_C TUid CMPXCollectionClientContext::PluginId() const
+    {
+    return iPluginUids[EContextBrowse];
+    }
+
+// ----------------------------------------------------------------------------
+// Handle async request from client
+// ----------------------------------------------------------------------------
+//
+EXPORT_C const CMPXCollectionPath& CMPXCollectionClientContext::Path() const
+    {
+    return *iBrowsePath;
+    }
+
+// ----------------------------------------------------------------------------
+// Cancel request
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::CancelRequest(
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    MPX_DEBUG3("CMPXCollectionClientContext::CancelRequest, this %08x, aCallback %08x",
+               this, aCallback);
+    iEngine.RemoveTask(aCallback);
+    }
+
+// ----------------------------------------------------------------------------
+// Add a client
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::AddClientL(
+    TThreadId aId,
+    CMPXMessageQueue* aMsgQueue)
+    {
+    iClientList->AddClientL(aId, aMsgQueue);
+    }
+
+// ----------------------------------------------------------------------------
+// Remove a client
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::RemoveClient(
+    const CMPXMessageQueue& aMsgQueue)
+    {
+    MPX_DEBUG2("CMPXCollectionClientContext::RemoveClient with the message queue 0x%08x",
+               &aMsgQueue);
+    TInt index(iClientList->Find(aMsgQueue));
+    if (KErrNotFound != index)
+        {
+        iClientList->RemoveClient(index);
+        }
+    if (!iClientList->ClientCount())
+        {
+        iEngine.RemoveContext(*this);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Gets the supported capabilities of the current browse plugin
+// ----------------------------------------------------------------------------
+//
+EXPORT_C TCollectionCapability CMPXCollectionClientContext::GetCapabilities()
+    {
+    TCollectionCapability cap(0);
+    if (iPluginUids[EContextBrowse]!=KNullUid)
+        {
+        LoadedPlugin(EContextBrowse)->GetCapabilities();
+        }
+    return cap;
+    }
+
+// ----------------------------------------------------------------------------
+// Find all aSync
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::FindAllL(
+    const CMPXMedia& aMedia,
+    CBufBase* aBuf,
+    MMPXCollectionEngineObserver* aCallback)
+    {
+    CMPXCollectionPlugin* plugin(NULL); // Not owned
+
+    // Increment the plugin reference count
+    ResolvePluginL(aMedia, plugin);
+    if (!plugin)
+        {
+        User::Leave(KErrNotFound);  // found nothing
+        }
+
+    iEngine.CleanupPluginPushL(plugin);
+
+    // Add the task
+    CMPXMedia* media = CMPXMedia::NewL(aMedia);
+    CleanupStack::PushL(media);
+    plugin->AddTaskL(EMcsFindAll, aCallback, this, 0, aBuf, plugin, media);
+    CleanupStack::Pop(media); // Ownership transferred
+
+    iEngine.PluginPop();
+    }
+
+// ----------------------------------------------------------------------------
+// Find all sync
+// ----------------------------------------------------------------------------
+//
+EXPORT_C CMPXMedia* CMPXCollectionClientContext::FindAllSyncL(
+    const CMPXMedia& aMedia,
+    const CBufBase& aBuf)
+    {
+    CMPXCollectionPlugin* plugin(NULL); // Not owned
+
+    // Increment the plugin reference count
+    ResolvePluginL(aMedia, plugin);
+    iEngine.CleanupPluginPushL(plugin);
+
+    CMPXMedia* result(NULL);
+    FindAllL(aMedia, aBuf, *plugin, &result, ETrue);
+
+    // Synchronous call, safe to release the plugin
+    CleanupStack::PopAndDestroy();    // plugin
+    return result;
+    }
+
+// ----------------------------------------------------------------------------
+// Set filter
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::SetFilterL(const CMPXFilter* aFilter)
+    {
+    delete iFilter;
+    iFilter = NULL;
+    iFilter = aFilter ? CMPXFilter::NewL(*aFilter) : NULL;
+    }
+
+// ----------------------------------------------------------------------------
+// Returns filter
+// ----------------------------------------------------------------------------
+//
+EXPORT_C const CMPXFilter* CMPXCollectionClientContext::Filter() const
+    {
+    return iFilter;
+    }
+
+// ----------------------------------------------------------------------------
+// Handle a command
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::CommandL(
+    TMPXCollectionCommand aCmd,
+    TInt aData)
+    {
+    switch( aCmd )
+        {
+        case EMcCmdRemoveAll:
+        case EMcCmdReCreateDB: // fall through
+        case EMcCmdDbCorrupted: // fall through
+            {
+            TUid uid = TUid::Uid(aData);
+
+            // Increment the plugin reference count
+            CMPXCollectionPlugin* plugin = iEngine.ResolvePluginL( uid );
+            iEngine.CleanupPluginPushL(plugin);
+
+            if( plugin )
+                {
+                // The trap is needed to keep the plugin reference count in sync
+                plugin->CommandL(aCmd);
+                }
+
+            CleanupStack::PopAndDestroy();    // plugin
+            break;
+            }
+        case EMcCmdSelect:
+            if (iBrowsePath->Levels()<=0)
+                {
+                User::Leave(KErrNotReady);
+                }
+            if (aData<0 || aData>=iBrowsePath->Count())
+                {
+                User::Leave(KErrArgument);
+                }
+            iBrowsePath->Set(aData);
+            iClientList->SendMsgL(
+                  TMPXCollectionMessage(TMPXCollectionMessage::EFocusChanged,
+                                        aData, aData));
+            iIndex = aData; // iIndex will always be up to date
+            iFocusItemId = iBrowsePath->Id();
+            break;
+        case EMcCmdCollectionInit:
+        case EMcCmdCollectionResyn:
+            {
+            TUid uid = TUid::Uid(aData);
+            // Increment the plugin reference count
+            CMPXCollectionPlugin* plugin = iEngine.ResolvePluginL(uid);
+            if( !plugin )
+                {
+                User::Leave(KErrArgument);
+                }
+
+            iEngine.CleanupPluginPushL(plugin);
+
+            plugin->AddTaskL(EMcsCommand, NULL, this, aCmd, NULL, plugin);
+
+            iEngine.PluginPop();
+            break;
+            }
+        default:
+            {
+            User::Leave(KErrNotSupported);
+            break;
+            }
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Handle a command
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXCollectionClientContext::CommandL(
+    const CMPXCommand& aCmd,
+    MMPXCollectionEngineObserver* aCallback,
+    const CMPXMessageQueue& aMsgQueue)
+    {
+    // Use the plugin id defined by the command object,
+    // or the browse context if collection id not defined
+    CMPXCollectionPlugin* plugin = NULL;
+    if (aCmd.IsSupported(KMPXCommandGeneralCollectionId))
+        {
+        TInt data = aCmd.ValueTObjectL<TInt>(KMPXCommandGeneralCollectionId);
+
+        // Increment the plugin reference count
+        plugin = iEngine.ResolvePluginL(TUid::Uid(data));
+        }
+    if (!plugin && iPluginUids[EContextBrowse]!=KNullUid)
+        {
+        plugin = LoadedPlugin(EContextBrowse);
+
+        // Increment the plugin reference count manually
+        iEngine.UsePlugin(plugin->Uid());
+        }
+   iEngine.CleanupPluginPushL(plugin);
+
+   TBool async(ETrue); // by default command is asynchronous
+   if (aCmd.IsSupported(KMPXCommandGeneralDoSync))
+       { // check if command is sync
+       async=!(aCmd.ValueTObjectL<TBool>(KMPXCommandGeneralDoSync));
+       }
+
+   if (async)
+        { // Async, cmd ownership transferred
+        if (!plugin)
+            {
+            User::Leave(KErrNotReady);
+            }
+
+        CMPXCommand* cmd = CMPXCommand::NewL(aCmd);
+        CleanupStack::PushL(cmd);
+        plugin->AddTaskL(EMcsCommandExt, aCallback, this, 0,
+                        NULL, plugin, cmd);
+        CleanupStack::Pop(cmd);
+
+        // Async, the plugin pointer passed through the task queue
+        iEngine.PluginPop();
+        }
+    else
+        {
+        DoHandleSyncCommandL(aCmd, aMsgQueue, plugin);
+        CleanupStack::PopAndDestroy();     // plugin
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Find a shareable client
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionClientContext::HasShareableClient(
+    TThreadId aId)
+    {
+    TBool ret(EFalse);
+    if (iModeId != KMcModePlaylist)
+        {
+        if (iClientList->IsClient(aId) )    // Just look for same thread
+            {
+            ret = ETrue;
+            }
+        }
+    return ret;
+    }
+
+// ----------------------------------------------------------------------------
+// Notifies all clients of events
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::NotifyL( TMPXCollectionBroadCastMsg aMsg,
+                                           TInt aData )
+    {
+    iClientList->SendMsgL(
+          TMPXCollectionMessage(TMPXCollectionMessage::EBroadcastEvent,
+                                aMsg,aData));
+    }
+
+const TUid& CMPXCollectionClientContext::ModeId() const
+    {
+    return iModeId;
+    }
+
+// ----------------------------------------------------------------------------
+// Initialises from file. iFile is not owned here
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::OpenL()
+    {
+    if (iPathUpdated)
+        {
+        ReOpenL();
+        }
+    else
+        {
+        CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+        MPX_ASSERT(plugin);
+        plugin->CompleteTask();
+        if (plugin->Callback())
+            {
+            plugin->Callback()->HandleOpen(iMedia,
+                                                   iIndex, ETrue,iMediaType);
+            plugin->SetCallback( NULL ); // Reset current observer
+            plugin->SetObserver(iEngine);
+            }
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Cancels all outstanding calls (tasks): plug-in should only have one
+// outstanding so that is canceled; the tasks are deleted and removed from the
+//  queue
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::BackL()
+    {
+    TInt d=iBrowsePath->Levels();
+    if(d>1)
+        {
+        iPathUpdated = EFalse;
+        // remove last item - the actual item selected
+        iBrowsePath->Back();
+        // preserve focus, since we're rebuilding this level
+        iIndex=iBrowsePath->Index();
+        iFocusItemId = iBrowsePath->Id();
+        ReOpenL();
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Re-open current level
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::ReOpenL()
+    {
+    // remove last item - go to the container which contained the item
+    if (iBrowsePath->Levels())
+        {
+        iBrowsePath->Back();
+        }
+    if (iBrowsePath->Levels() == 0)
+        {
+        //Plugin must be resolved ealier
+        CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+        plugin->CompleteTask();
+        InitL(plugin->Callback());
+        }
+    else
+        {
+        DoPluginOpenL();
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Retrieve media info based on a find criteria
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::FindAllL(
+    const CMPXMedia& aMedia,
+    const CBufBase& aBuf,
+    CMPXCollectionPlugin& aPlugin,
+    CMPXMedia** aResult /*= NULL*/,
+    TBool aSync /*= EFalse*/)
+    {
+    // Find critera and return attributes
+    //
+    RArray<TMPXAttribute> attrs;
+    CleanupClosePushL(attrs);
+    ::CreateFromBufferL(aBuf, attrs);
+
+    // Do the query
+    if (!aSync)
+        {
+        aPlugin.SetObserver(*this);
+        aPlugin.FindAllL(aMedia,attrs.Array());
+        }
+    else
+        {
+        *aResult = aPlugin.FindAllSyncL(aMedia,attrs.Array());
+        }
+    CleanupStack::PopAndDestroy( &attrs );
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::HandleMessage
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleMessage(
+    CMPXMessage* aMsg,
+    TInt aError)
+    {
+    MPX_FUNC_EX("CMPXCollectionClientContext::HandleMessage");
+    DoHandleMessage(aMsg, aError, ETrue);
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::HandleMessage
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleMessage(
+    CMPXMessage* aMsg,
+    TInt aError,
+    TBool aNotifyOthers)
+    {
+    MPX_DEBUG3("-->CMPXCollectionClientContext::HandleMessage this %08x, %d", this, aNotifyOthers);
+    if (!aMsg)
+        {
+        iClientList->SendMsg(aMsg, aError);
+        if (aNotifyOthers)
+            {
+            iEngine.NotifyChange(*this, aMsg, aError);
+            }
+        }
+    else
+        {
+        if (aMsg->IsSupported(KMPXMessageGeneralId))
+            {
+            if (aMsg->IsSupported(KMPXMessageArrayContents))
+                {
+                TBool pathUpdated(EFalse);
+                const CMPXMessageArray* messageArray =
+                    aMsg->Value<CMPXMessageArray>(KMPXMessageArrayContents);
+                if (messageArray)
+                    {
+                    TInt count = messageArray->Count();
+
+                    MPX_DEBUG2("%d messages:", count);
+
+                    for (TInt i=0; i<count; i++)
+                        {
+                        const CMPXMessage* message =(*messageArray)[i];
+                        if (message)
+                            {
+                            TRAP_IGNORE(DoHandleMessageL(*message, KErrNone));
+                            pathUpdated |=iPathUpdated;
+                            }
+                        }
+                    iPathUpdated = pathUpdated;
+                    }
+                // Notify other context, collection is changed.
+                // Send the batched version
+                //
+                if (aNotifyOthers)
+                    {
+                    iEngine.NotifyChange(*this,
+                                         const_cast<CMPXMessage*>(aMsg),
+                                         aError);
+                    }
+                }
+            else
+                {
+                TRAP_IGNORE(DoHandleMessageL(*aMsg, aError));
+
+                // Notify other context, collection is changed.
+                if (aNotifyOthers)
+                    {
+                    iEngine.NotifyChange(*this, aMsg, aError);
+                    }
+                }
+
+            // Broadcast change messages to the clients to handle
+            //
+            if( iModeId == KMcModePlaylist )
+                {
+                // Playlist should not be handling msgs during refresh
+                if( !iEngine.IsRefreshing() )
+                    {
+                    iClientList->SendMsg( aMsg, aError );
+                    }
+                }
+            else
+                {
+                // Other modes receive msg all the time
+                iClientList->SendMsg( aMsg, aError );
+                }
+
+            // Batched collection change messages and only send this once
+            // Path changed isn't sent to the UI during refresh
+            //
+            if (iPathUpdated)
+                {
+                MPX_DEBUG1("CMPXCollectionClientContext::DoHandleMessageL detected path changed");
+                MPX_DEBUG_PATH(*iBrowsePath);
+                // notify clients to refresh
+                TRAP_IGNORE(iClientList->SendMsgL(
+                    TMPXCollectionMessage(TMPXCollectionMessage::EPathChanged,
+                                          EMcPathChangedByCollectionChange)));
+                }
+            }
+        }
+
+    MPX_DEBUG3("<--CMPXCollectionClientContext::HandleMessage this %08x, %d", this, aNotifyOthers);
+    }
+
+// ----------------------------------------------------------------------------
+// Handle collection item change message
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleMessageL(
+    const CMPXMessage& aMsg,
+    TInt /*aError*/)
+    {
+    MPX_DEBUG2("-->CMPXCollectionClientContext::DoHandleMessageL this %08x", this);
+    TInt browseLevels = iBrowsePath->Levels(); // browse depth
+
+    if (aMsg.ValueTObjectL<TMPXMessageId>(KMPXMessageGeneralId)==
+        KMPXMessageIdItemChanged)
+        {
+        if (!aMsg.IsSupported(KMPXMessageCollectionId) ||
+            !aMsg.IsSupported(KMPXMessageChangeEventType) ||
+            !aMsg.IsSupported(KMPXMessageMediaGeneralCategory) ||
+            !aMsg.IsSupported(KMPXMessageMediaGeneralId))
+            {
+            User::Leave(KErrArgument);
+            }
+
+        TUid collectionId = aMsg.ValueTObjectL<TUid>(KMPXMessageCollectionId);
+
+        TMPXChangeEventType changeType =
+            aMsg.ValueTObjectL<TMPXChangeEventType>(KMPXMessageChangeEventType);
+
+        TMPXGeneralCategory category =
+            aMsg.ValueTObjectL<TMPXGeneralCategory>(KMPXMessageMediaGeneralCategory);
+
+        TMPXItemId itemId = aMsg.ValueTObjectL<TMPXItemId>(KMPXMessageMediaGeneralId);
+
+        TMPXItemId deprecatedId(0);
+        if (aMsg.IsSupported(KMPXMessageMediaDeprecatedId))
+            {
+            deprecatedId = aMsg.ValueTObjectL<TMPXItemId>(KMPXMessageMediaDeprecatedId);
+            }
+
+        MPX_DEBUG5("CMPXCollectionClientContext::DoHandleMessageL colId %08x, id %d, deprecatedId %d, type %d",
+                  collectionId.iUid, itemId.iId1, deprecatedId.iId1, changeType);
+
+
+        if(!iEngine.IsRefreshing())
+            {
+            // Invalidate path
+            TInt pct = KErrNotFound; // path change type
+            if (changeType == EMPXItemInserted && category != EMPXPlaylist)
+                {
+                pct = CMPXCollectionPath ::EAdded;
+                }
+            else if (changeType == EMPXItemModified && category != EMPXPlaylist)
+                {
+                pct = CMPXCollectionPath ::EModified;
+                }
+            else if (changeType == EMPXItemDeleted)
+                {
+                pct = CMPXCollectionPath ::EDeleted;
+                }
+            else if ((changeType == EMPXItemInserted || changeType == EMPXItemModified) &&
+                     category == EMPXPlaylist)
+                {
+                pct = CMPXCollectionPath ::EGroupModified;
+                }
+            if( changeType == EMPXItemModified && category == EMPXCollection )
+                {
+                // Overwrite previous type
+                pct = CMPXCollectionPath ::EGroupModified;
+                }
+
+            if (KErrNotFound != pct)
+                { // update browse path
+                CMPXCollectionPath::TMPXCollectionPathChange ct =
+                    static_cast<CMPXCollectionPath::TMPXCollectionPathChange>(pct);
+                TInt pUpdated(CMPXCollectionPath::EPathUnchanged);
+
+                // If browse context and browse context is being modified
+                //
+                if (iModeId != KMcModePlaylist && browseLevels &&
+                    iBrowsePath->Id(0) == collectionId.iUid )
+                    { // check the browse path
+                    TInt tmpIndex(0);
+
+                    pUpdated = iBrowsePath->HandleChange(
+                                       collectionId, itemId, deprecatedId, ct,
+                                       tmpIndex);
+
+                    // Take the updated selection index only if it was affected
+                    // We need to store the TMPXItemId for iIndex because
+                    // a previous collection change would have removed the
+                    // top level of the collection path
+                    //
+                    iPathUpdated = iPathUpdated ||
+                                   (pUpdated != CMPXCollectionPath::EPathUnchanged);
+                    if( pUpdated )
+                        {
+                        // Only update the selection index if the itemid that
+                        // was modified is equal to the one selected
+                        //
+                        // OR take in the index if clipped the collection path
+                        // (pUpdated == KPathClipped )
+                        //
+                        if( pUpdated == CMPXCollectionPath::EPathClipped )
+                            {
+                            iIndex = tmpIndex;
+                            iFocusItemId = KMPXInvalidItemId;
+                            }
+                        else if( iFocusItemId.ApproxEqual(itemId ) &&
+                                 ( iFocusItemId != KMPXInvalidItemId )  )
+                            {
+                            // Also make sure the index that came back from
+                            // checking the collection path isn't -1
+                            //
+                            // The only case where the selected item will shift
+                            // is when we are deleting that item. updaing the item or adding
+                            // an item to the current path should not move the focus to another
+                            // item
+                            //
+                            if( tmpIndex != -1 && changeType == EMPXItemDeleted)
+                                {
+                                if( tmpIndex == iBrowsePath->Count()-1 )
+                                    {
+                                    // Last item, focus on previous
+                                    iIndex = tmpIndex-1;
+                                    iFocusItemId = iBrowsePath->IdOfIndex( tmpIndex-1 );
+                                    }
+                                else
+                                    {
+                                    // Not last, focus on next
+                                    iFocusItemId = iBrowsePath->IdOfIndex( tmpIndex+1 );
+                                    }
+                                }
+                            }
+                        else if( iFocusItemId.ApproxEqual( deprecatedId ) &&
+                                 changeType == EMPXItemModified )
+                            {
+                            // If we are modifying the item id of an item currently in focus
+                            // We move the focus to the new item id
+                            //
+                            iFocusItemId = itemId;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    else
+        {
+        // do nothing
+        }
+    MPX_DEBUG2("<--CMPXCollectionClientContext::DoHandleMessageL this %08x", this);
+    }
+
+// ----------------------------------------------------------------------------
+// Handle open event
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleOpen(
+    CMPXMedia* aMedia,
+    TInt aErr)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::HandleOpen with media returned");
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+    MPX_ASSERT(plugin);
+
+    // Callback and Task Completion
+    //
+    MMPXCollectionEngineObserver* callback(NULL);
+    TBool openTask(EFalse);
+    TInt task = plugin->Task();
+    if( task == EMcsOpen || task == EMcsOpenPath ||
+        task == EMcsBack || task == EMcsOpenIndex )
+        {
+        callback = plugin->Callback();
+        openTask = ETrue;
+        }
+
+    // Cachable flag
+    //
+    TBool cache(EFalse);
+    if( iBrowsePath &&
+        iBrowsePath->Levels() >= 1 )
+        {
+        TMPXItemId id(iBrowsePath->Id(CMPXCollectionPath::ECollectionUid) );
+        cache = iEngine.PluginCacheable( TUid::Uid( id ) );
+        }
+
+    TRAPD(err, DoHandleOpenL(aMedia, NULL, callback, aErr, cache, openTask));
+    if (err && openTask)
+        {
+        HandleError(*plugin, err);
+        }
+    else if( openTask )
+        {
+        plugin->CompleteTask();
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Handle open event
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleOpen(
+    CMPXMedia* aMedia,
+    const CMPXCollectionPath* aPath,
+    TInt aErr)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::HandleOpen with media returned");
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+    MPX_ASSERT(plugin);
+
+    // Callback and Task Completion
+    //
+    MMPXCollectionEngineObserver* p(NULL);
+    TBool openTask(EFalse);
+    TInt task = plugin->Task();
+    if( task == EMcsOpen || task == EMcsOpenPath ||
+        task == EMcsBack || task == EMcsOpenIndex )
+        {
+        p = plugin->Callback();
+        openTask = ETrue;
+        }
+
+    // Cachable flag
+    //
+    TBool cache(EFalse);
+    if( iBrowsePath &&
+        iBrowsePath->Levels() >= 1 )
+        {
+        TMPXItemId id(iBrowsePath->Id(CMPXCollectionPath::ECollectionUid) );
+        cache = iEngine.PluginCacheable( TUid::Uid( id ) );
+        }
+
+    // Handle OpenL for Media
+    //
+    TRAPD(err, DoHandleOpenL(aMedia, aPath, p, aErr, cache, openTask));
+    if (err && openTask)
+        {
+        HandleError(*plugin, err);
+        }
+    else if( openTask )
+        {
+        plugin->CompleteTask();
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Handle open event
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleOpenL(
+    CMPXMedia* aMedia,
+    const CMPXCollectionPath* aPath,
+    MMPXCollectionEngineObserver* aCallback,
+    TInt aErr,
+    TBool aAddToCache,
+    TBool aOpenTask )
+    {
+    MPX_FUNC("CMPXCollectionClientContext::DoHandleOpen with media returned");
+
+    // Make sure if we are updating the results the paths are aligned!
+    //
+    TBool openUpdate(EFalse);
+    if( !aOpenTask )
+        {
+        openUpdate = ETrue;
+        iCacheMedia = ETrue;
+        if( aPath &&
+            aPath->Levels() != iBrowsePath->Levels() )
+            {
+            User::Leave( KErrArgument );
+            }
+        }
+
+    // Add to cache
+    //
+    if ( iBrowsePath->Levels() && aAddToCache && aMedia && iCacheMedia )
+        {
+        // manage the priority
+        CMPXCollectionCache::TCachePriority priority(CMPXCollectionCache::EPriorityNormal);
+        if ((1 == iBrowsePath->Levels()) ||
+            ((2 == iBrowsePath->Levels()) && ((iBrowsePath->Id(1)).iId1 == 0)))
+            {
+            priority = CMPXCollectionCache::EPriorityHigh;
+            }
+
+        if( !aOpenTask )
+            {
+            // Update the current browse path
+            //
+            CMPXCollectionPath* container = iBrowsePath->ContainerPathL();
+            CleanupStack::PushL( container );
+
+            // Add the data to the generic cache and get the latest copy
+            //
+            aMedia = AddToCache( *container,
+                         aMedia->Attributes(),
+                         *aMedia,
+                         ETrue,
+                         priority);
+            CleanupStack::PopAndDestroy( container );
+            }
+        else
+            {
+            AddToCache( *iBrowsePath,
+                         aMedia->Attributes(),
+                         *aMedia,
+                         ETrue,
+                         priority);
+            }
+        }
+
+    // Current client context browse path media
+    //
+    iMediaType = KMPXCollectionEntries;
+    if (iMedia != aMedia)
+        { // new media
+        delete iMedia;
+        iMedia = NULL;
+        if (aMedia)
+            {
+            iMedia = CMPXMedia::NewL(*aMedia);
+            }
+        }
+
+    TInt n(0);
+    if (aMedia && KErrNone == aErr)
+        {  // Update the path
+        RArray<TInt> selectionIndicies;
+        CleanupClosePushL( selectionIndicies );
+
+        if (aPath)
+            { // Update path from aPath
+            if ( aMedia->IsSupported (KMPXCollectionOpenLAllResultRange))
+                {
+                iBrowsePath->Back ();
+                RArray<TMPXItemId> ids;
+                CleanupClosePushL (ids);
+                RArray<TMPXOpenDataBlock> datablocks;
+                CleanupClosePushL (datablocks);
+                // De-serialize from global data, would be good to have global arrays
+                //
+                const TDesC
+                        & buf = aMedia->ValueText (KMPXCollectionOpenLAllResultRange);
+                CBufBase* buffer(NULL);
+                MPXUser::CreateBufferL ( buf, buffer);
+                CleanupStack::PushL ( buffer);
+                ::CreateFromBufferL ( *buffer, datablocks);
+                CleanupStack::PopAndDestroy ( buffer);
+
+                if ( aMedia->IsSupported (KMPXMediaArrayContents))
+                    {
+                    const CMPXMediaArray
+                            * mediaArray = aMedia->Value<CMPXMediaArray> (KMPXMediaArrayContents);
+                    User::LeaveIfNull (const_cast<CMPXMediaArray*>(mediaArray));
+                    TInt dataCount = mediaArray->Count ();
+                    TInt rangeCount = datablocks.Count ();
+                    for (TInt index = 0; index < dataCount; ++index)
+                        {
+                        CMPXMedia* media = mediaArray->AtL(index);
+
+                        //Check range
+                        TBool validItem(EFalse);
+                        MPX_ASSERT(rangeCount > 0);
+                        for (TInt rangeIndex = 0; rangeIndex < rangeCount; ++rangeIndex)
+                            {
+                            TInt offset = datablocks[rangeIndex].iOffset;
+                            TInt size = datablocks[rangeIndex].iSize;
+                            if ( (index >= offset) && (index < offset + size))
+                                {
+                                validItem = ETrue;
+                                break;
+                                }
+                            }
+                        
+                        if (validItem)
+                            {
+                            const TMPXItemId
+                                    id = media->ValueTObjectL<TMPXItemId> (KMPXMediaGeneralId);
+                            ids.AppendL (id);
+                            }
+                        else
+                            {
+                            ids.AppendL (KMPXInvalidItemId);
+                            }
+                        }
+                    iBrowsePath->AppendL (ids.Array ());
+                    }
+                CleanupStack::PopAndDestroy (&datablocks);
+                CleanupStack::PopAndDestroy (&ids);
+                }
+            else
+                {
+                delete iBrowsePath;
+                iBrowsePath = NULL;
+                iBrowsePath = CMPXCollectionPath::NewL ( *aPath);
+                }
+            }
+        else
+            { // Update path from media
+            RArray<TMPXItemId> ids;
+            CleanupClosePushL(ids);
+            if( aMedia->IsSupported(KMPXMediaArrayContents) )
+                {
+                const CMPXMediaArray* mediaArray=aMedia->Value<CMPXMediaArray>(
+                                                            KMPXMediaArrayContents);
+                User::LeaveIfNull(const_cast<CMPXMediaArray*>(mediaArray));
+                n=mediaArray->Count();
+                for (TInt i=0;i<n;++i)
+                    {
+                    CMPXMedia* media=mediaArray->AtL(i);
+                    const TMPXItemId id=media->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId);
+                    ids.AppendL(id);
+
+                    // Try to look for the selection that we want
+                    if( iFocusItemId == id )
+                        {
+                        selectionIndicies.AppendL( i );
+                        }
+                    }
+                }
+
+            iBrowsePath->AppendL(ids.Array());
+            CleanupStack::PopAndDestroy(&ids);
+            }
+
+        // List of items
+        const TArray<TMPXItemId>& items = iBrowsePath->Items();
+
+        // OpenL Media has been updated, update the itemid
+        // based on the current index
+        if( openUpdate )
+            {
+            // Check if we have item id first
+            //
+            if( iFocusItemId != KMPXInvalidItemId )
+                {
+                TInt index = iBrowsePath->IndexOfId( iFocusItemId );
+                if( index != KErrNotFound )
+                    {
+                    iIndex = index;
+                    }
+                }
+
+            // Then update based on index
+            if( iIndex >=0 && iIndex < iBrowsePath->Count() )
+                {
+                iBrowsePath->Set(iIndex);
+                iFocusItemId = iBrowsePath->Id();
+                }
+            }
+        else if( aMedia->IsSupported( KMPXCollectionOpenLResultRange ) )
+            {
+            TMPXOpenDataBlock data =
+                aMedia->ValueTObjectL<TMPXOpenDataBlock>(KMPXCollectionOpenLResultRange);
+            if( iBrowsePath->Count() &&
+                iIndex >= 0 &&
+                iIndex < iBrowsePath->Count() )
+                {
+                // If all blank items then we don't change index yet
+                // Wait until we have re-opened the partial data
+                if( data.iOffset != KErrNotFound )
+                    {
+                    // Not supported, stay with current focus
+                    iBrowsePath->Set(iIndex);
+                    iFocusItemId = iBrowsePath->Id();
+                    }
+                }
+            }
+        // If this selection appears more than once, then we have to find
+        // the closest matching index to what we had before
+        //
+        //
+        else if( selectionIndicies.Count() > 1 &&
+                 iFocusItemId != KMPXInvalidItemId )
+            {
+            TInt best(selectionIndicies[0]);
+            TInt idAppearance( selectionIndicies.Count() );
+            for( TInt i=0; i<idAppearance; ++i )
+                {
+                if( Abs(selectionIndicies[i]-iIndex) <= Abs(best-iIndex) )
+                    {
+                    best = selectionIndicies[i];
+                    }
+                }
+            iIndex = best;
+            iBrowsePath->Set(iIndex);
+            }
+        // Always set by item id if we have it
+        // This is needed if an item was added to the current browse level
+        // We still want to select the item we were previously at
+        //
+        else if( iFocusItemId != KMPXInvalidItemId )
+            {
+            TInt index = iBrowsePath->IndexOfId(iFocusItemId);
+            if (KErrNotFound != index)
+                {
+                iBrowsePath->Set(index);
+                }
+            else if( n > 0 ) // Focus item has been deleted, select next
+                {
+                // Bounds check for largest and smallest
+                if( iIndex >= n )
+                    {
+                    iIndex = n-1;
+                    }
+                if( iIndex < 0 )
+                    {
+                    iIndex = 0;
+                    }
+                iBrowsePath->Set(iIndex);
+                iFocusItemId = iBrowsePath->Id();
+                }
+            iIndex = iBrowsePath->Index();
+            }
+        else
+            {
+            // Bounds checking for iIndex
+            if( iIndex >= n && iIndex > 0 )
+                {
+                iIndex = n-1;
+
+                // Just in case if ids has 0 items
+                if( iIndex > 0 && iIndex < items.Count() )
+                    {
+                    iFocusItemId = items[iIndex];
+                    }
+                }
+
+            if (iBrowsePath->Levels()>0 && iIndex>=0 &&
+                iIndex<iBrowsePath->Count())
+                {
+                iBrowsePath->Set(iIndex);
+                }
+            iFocusItemId = iBrowsePath->Id();
+            }
+        CleanupStack::PopAndDestroy(&selectionIndicies);
+        }
+
+    TInt err;
+    if( !iPathUpdated )
+        {
+        // Normal open mode
+        err = KErrNone == aErr ? KMPXPathUpdated : aErr;
+        }
+    else
+        {
+        // Updates open mode, should be returning entries
+        err = KErrNone == aErr ? KMPXCollectionEntries : aErr;
+        }
+
+    // Should not be broadcasting container opened messages
+    // When we are only rebuilding the leaf level
+    //
+    if( !iPathUpdated && aOpenTask )
+        {
+        if( err >= KErrNone)
+            {
+            iClientList->SendMsgL(
+                TMPXCollectionMessage(TMPXCollectionMessage::EPathChanged,
+                                      EMcPathChangedByOpen,
+                                      EMcContainerOpened));
+            }
+        }
+
+    iPathUpdated = EFalse;
+
+    // Notes: there should no leave function called after aCallback->HandleOpen
+    if (aCallback)
+        {  // Complete open with error or PathUpdated
+        aCallback->HandleOpen(iMedia, iIndex, ETrue, err);
+        if (iPluginUids[EContextBrowse]!=KNullUid)
+            {
+            CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+            MPX_ASSERT(aCallback == plugin->Callback());
+            plugin->SetCallback(NULL);
+            plugin->SetObserver(iEngine);
+            }
+        }
+    MPX_DEBUG_PATH(*iBrowsePath);
+    }
+
+// ----------------------------------------------------------------------------
+// Handle open event
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleOpen(
+   CMPXCollectionPath* aPath,
+   TInt aErr)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::HandleOpen with path returned");
+    TRAPD(err, DoHandleOpenL(aPath, aErr));
+    if (err)
+        {
+        HandleError(*(LoadedPlugin(EContextBrowse)), err);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Handle open event
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleOpenL(
+    CMPXCollectionPath* aPath,
+    TInt aErr)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::DoHandleOpen with path returned");
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+    MPX_ASSERT(plugin);
+
+    plugin->CompleteTask();
+
+    if (iBrowsePath != aPath && KErrNone == aErr)
+        {
+        delete iBrowsePath;
+        iBrowsePath = NULL;
+        MPX_ASSERT(aPath);
+        iBrowsePath = CMPXCollectionPath::NewL(*aPath);
+        }
+    iMediaType = KMPXCollectionPath;
+    iIndex = iBrowsePath->Index();
+    iFocusItemId = iBrowsePath->Id();
+
+    TInt err = KErrNone == aErr ? KMPXPathUpdated : aErr;
+
+    // Complete msg
+    // Complete open with error or PathUpdated
+    plugin->Callback()->HandleOpen(iMedia, iIndex, ETrue, err);
+    plugin->SetCallback(NULL);
+    plugin->SetObserver(iEngine);
+    if (aPath->OpenNextMode() != EMPXOpenNoPlaylist)
+         //
+         // The request was NOT to play, but the plug-in returned
+         // the path implying that we should play; so we don't
+         // send it
+         //
+        {
+        iClientList->SendMsgL(
+               TMPXCollectionMessage(TMPXCollectionMessage::EPathChanged,
+                                     EMcPathChangedByOpen,
+                                     EMcItemOpened));
+        }
+
+    MPX_DEBUG_PATH(*iBrowsePath);
+    }
+
+// ----------------------------------------------------------------------------
+// Callback of retrieving extended media property
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleMedia(
+    CMPXMedia* aMedia,
+    TInt aError)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::HandleMedia");
+
+    CMPXMedia* ret( aMedia );
+
+    TBool pluginCacheable(EFalse);
+    if( iMediaPath )
+        {
+        TMPXItemId id(iMediaPath->Id(CMPXCollectionPath::ECollectionUid) );
+        pluginCacheable = iEngine.PluginCacheable( TUid::Uid( id ) );
+
+        // if media returned was resulted from multiple selections we don't cache the
+        // results as it's very hard to reuse it anyway
+        if(iMediaPath->Selection().Count() > 0)
+            {
+            pluginCacheable = EFalse;
+            }
+        }
+
+    // Cache the media
+    if ( !aError && aMedia && iCacheMedia && iMediaPath && pluginCacheable )
+        {
+        TMPXItemId id( iMediaPath->Id(
+                            CMPXCollectionPath::ECollectionUid ));
+        ret = AddToCache( *iMediaPath, aMedia->Attributes(), *aMedia );
+        }
+
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextMedia);
+
+    plugin->CompleteTask();
+    MPX_ASSERT(plugin->Callback());
+    plugin->Callback()->HandleMedia(ret, aError);
+    plugin->SetCallback(NULL); // Reset current observer
+    plugin->SetObserver(iEngine);
+    }
+
+// ----------------------------------------------------------------------------
+// Callback of async CommandL
+// ----------------------------------------------------------------------------
+//
+ void CMPXCollectionClientContext::HandleCommandComplete(
+    CMPXCommand* aCommandResult,
+    TInt aError)
+    {
+    MPX_FUNC_EX("CMPXCollectionClientContext::HandleCommandComplete");
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextMedia);
+
+    plugin->CompleteTask();
+    MPX_ASSERT(plugin->Callback());
+    plugin->Callback()->HandleCommandComplete(aCommandResult, aError);
+    plugin->SetCallback(NULL); // Reset current observer
+    plugin->SetObserver(iEngine);
+    }
+
+// ----------------------------------------------------------------------------
+// Handle find all
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleFindAll(
+    CMPXMedia* aMedia,
+    TInt aError)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::HandleFindAll");
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextMedia);
+    plugin->CompleteTask();
+    MPX_ASSERT(plugin->Callback());
+    plugin->Callback()->HandleFindAll(aMedia, aError);
+    plugin->SetCallback(NULL); // Reset current observer
+    plugin->SetObserver(iEngine);
+    }
+
+// ----------------------------------------------------------------------------
+// Handle delete all
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleRemove(
+    const CDesCArray& aUriArray,
+    TInt aError)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::HandleRemove");
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextRemove);
+
+    plugin->CompleteTask();
+    MPX_ASSERT(plugin->Callback());
+    plugin->Callback()->HandleRemove(aUriArray, aError);
+    plugin->SetCallback(NULL); // Reset current observer
+    plugin->SetObserver(iEngine);
+    }
+
+// ----------------------------------------------------------------------------
+// Execute a async task
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::ExecuteTask(
+    TInt aTask,
+    TInt aParamData,
+    TAny* aPtrData,
+    const CBufBase& aBuf,
+    TAny* aCallback,
+    CBase* aCObject1,
+    CBase* aCObject2)
+    {
+    MPX_DEBUG2("-->CMPXCollectionClientContext::ExecuteTask %d", aTask);
+    CMPXCollectionPlugin* plugin(NULL);
+    TRAPD(err, ExecuteTaskL(aTask, aParamData, aPtrData, aCallback, aBuf,
+                            aCObject1,aCObject2,plugin));
+    if (KErrNone !=err)
+        {
+        HandleError(*plugin, err, EFalse);
+        }
+    MPX_DEBUG1("<--CMPXCollectionClientContext::ExecuteTask");
+    }
+
+// ----------------------------------------------------------------------------
+// Indicates that a task was terminated with an error
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleTaskError(
+    TInt aTask,
+    TAny* aPtrData,
+    TAny* aCallback,
+    TInt aError)
+    {
+    MPX_FUNC_EX("CMPXCollectionClientContext::HandleTaskError");
+    CMPXCollectionPlugin* plugin(NULL);
+    switch (aTask)
+        {
+        case EMcsOpen:
+        case EMcsBack:
+        case EMcsOpenIndex:
+            plugin = LoadedPlugin(EContextBrowse);
+            plugin->SetObserver(*this);
+            plugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            HandleError(*plugin, aError, EFalse);
+            break;
+        case EMcsOpenPath:
+        case EMcsMediaByPath:
+        case EMcsCommandExt:
+        case EMcsRemovePath:
+        case EMcsFindAll:
+        case EMcsCommand:
+            plugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+            plugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            HandleError(*plugin, aError, ETrue);
+            break;
+        default:
+            break;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Initialize before first open
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::InitL(MMPXCollectionEngineObserver* aCallback)
+    {
+    MPX_DEBUG1("CMPXCollectionClientContext::InitL() <---");
+    delete iMedia;
+    iMedia = NULL;
+    delete iBrowsePath;
+    iBrowsePath = NULL;
+    RArray<TInt> supportedIds;
+    CleanupClosePushL(supportedIds);
+    supportedIds.AppendL(KMPXMediaIdContainer);
+    supportedIds.AppendL(KMPXMediaIdGeneral);
+    iMedia=CMPXMedia::NewL(supportedIds.Array());
+    iBrowsePath = CMPXCollectionPath::NewL();
+    CleanupStack::PopAndDestroy(&supportedIds);
+
+    iEngine.ListPluginsL(*iMedia, iUids.Array());
+    if (iPluginUids[EContextBrowse]!=KNullUid )
+        {
+        MPX_DEBUG1("CMPXCollectionClientContext::InitL() Collection changed");
+        iClientList->SendMsgL(
+            TMPXCollectionMessage(TMPXCollectionMessage::ECollectionChanged,0,0));
+        }
+
+    // sets the browse plugin to NULL
+    SetPlugin(EContextBrowse, NULL);
+
+    // Update collection path, and buffer, send path update msg,
+    // complete request
+    TRAPD(err, DoHandleOpenL(iMedia, NULL, aCallback, KErrNone, ETrue, ETrue));
+    if (err)
+        {
+        MPX_DEBUG2("CMPXCollectionClientContext::InitL() Complete Open %i", err);
+        aCallback->HandleOpen(iMedia, // Not used
+                              iIndex,
+                              ETrue, err);
+        }
+    MPX_DEBUG1("CMPXCollectionClientContext::InitL() --->");
+    }
+
+// ----------------------------------------------------------------------------
+// Execute a async task
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::ExecuteTaskL(
+    TInt aTask,
+    TInt aParamData,
+    TAny* aPtrData,
+    TAny* aCallback,
+    const CBufBase& aBuf,
+    CBase* aCObject1,
+    CBase* aCObject2,
+    CMPXCollectionPlugin*& aPlugin)
+    {
+    MPX_DEBUG4("CMPXCollectionClientContext::ExecuteTaskL 0x%08x, task %d, aParam %d",
+               this, aTask, aParamData);
+    switch (aTask)
+        {
+        case EMcsOpen:
+            {
+            aPlugin = LoadedPlugin(EContextBrowse);
+            aPlugin->SetObserver(*this);
+            aPlugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            OpenL();
+            break;
+            }
+        case EMcsOpenPath:
+            {
+            // Set plugin first for handling error
+            TUid oldUid = iPluginUids[EContextBrowse];
+            aPlugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+            // Set plugin and task pointer for browse context
+            SetPlugin(EContextBrowse, aPlugin);
+            iPathUpdated = EFalse;
+            aPlugin->SetObserver(*this);
+            aPlugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            CMPXCollectionPath* path = static_cast<CMPXCollectionPath*>(aCObject1);
+            delete iBrowsePath;
+            iBrowsePath = NULL;
+            iBrowsePath = CMPXCollectionPath::NewL(*path);
+            SetPathOpenMode(*iBrowsePath, static_cast<TMPXOpenMode>(aParamData));
+
+            // If the browsing plugin is changing, need to broadcast the
+            // collection changed message
+            if (iPluginUids[EContextBrowse] != oldUid)
+                {
+                // no leave before plugin API OpenL
+                TRAP_IGNORE(iClientList->SendMsgL(
+                    TMPXCollectionMessage(
+                         TMPXCollectionMessage::ECollectionChanged,0,iPluginUids[EContextBrowse].iUid)));
+                }
+            iIndex=0;
+            iFocusItemId = KMPXInvalidItemId;
+            DoPluginOpenL();
+            break;
+            }
+        case EMcsOpenIndex:
+            {
+            // Internalize mode
+            TInt mode = (TInt)aPtrData;
+            aPlugin = LoadedPlugin(EContextBrowse);
+            aPlugin->SetObserver(*this);
+            aPlugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            iBrowsePath->Set(aParamData);
+            SetPathOpenMode(*iBrowsePath,static_cast<TMPXOpenMode>(mode));
+            iIndex=0;
+            iFocusItemId = KMPXInvalidItemId;
+            DoPluginOpenL();
+            break;
+            }
+        case EMcsBack:
+            {
+            aPlugin = LoadedPlugin(EContextBrowse);
+            aPlugin->SetObserver(*this);
+            aPlugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            BackL();
+            break;
+            }
+        case EMcsMediaByPath:
+            {
+            // Setup plugin first for handle error
+            aPlugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+            SetPlugin(EContextMedia, aPlugin);
+
+            aPlugin->SetObserver(*this);
+            aPlugin->SetCallback(
+                        static_cast<MMPXCollectionEngineObserver*>(aCallback));
+            // Media path
+            CMPXCommand* cmd = reinterpret_cast<CMPXCommand*>(aCObject1);
+            MPX_ASSERT(cmd->IsSupported(KMPXCommandGeneralTargetIds));
+            MPX_ASSERT(cmd->IsSupported(KMPXCommandMediaAttributeSpecs));
+            CMPXCollectionPath* path = reinterpret_cast<CMPXCollectionPath*>(aCObject2);
+            CMPXAttributeSpecs* specs =
+                cmd->Value<CMPXAttributeSpecs>(KMPXCommandMediaAttributeSpecs);
+            User::LeaveIfNull(specs);
+            CMPXAttributeSpecs* filter =
+                cmd->Value<CMPXAttributeSpecs>(KMPXCommandMediaFilter);
+            User::LeaveIfNull(filter);
+            TCapabilitySet caps = cmd->ValueTObjectL<TCapabilitySet>(KMPXCommandMediaCapbilitySet);
+            // Ask plugin for media
+            DoPluginMediaL( *path, caps, *specs, *filter );
+            break;
+            }
+        case EMcsRemovePath:
+            {
+            aPlugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+            // set the remove plugin
+            SetPlugin(EContextRemove, aPlugin);
+
+            aPlugin->SetObserver(*this);
+            CMPXCollectionPath* path =
+                reinterpret_cast<CMPXCollectionPath*>(aCObject1);
+            aPlugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            aPlugin->RemoveL( *path );
+            break;
+            }
+        case EMcsFindAll:
+            {
+            aPlugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+            // set the media plugin
+            SetPlugin(EContextMedia, aPlugin);
+
+            aPlugin->SetCallback(
+                    reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback));
+            CMPXMedia* media = static_cast<CMPXMedia*>(aCObject1);
+            FindAllL(*media, aBuf, *aPlugin);
+            break;
+            }
+        case EMcsCommand:
+            {
+            TMPXCollectionCommand cmd =
+                static_cast<TMPXCollectionCommand>( aParamData );
+            switch( cmd )
+                {
+                case EMcCmdCollectionInit:
+                case EMcCmdCollectionResyn:
+                    // Decrement the old plugin reference count
+                    aPlugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+                    SetPlugin(EContextMedia, aPlugin);
+
+                    if( aPlugin )
+                        {
+                        aPlugin->SetObserver(*this);
+                        aPlugin->CommandL( cmd );
+                        aPlugin->SetObserver(iEngine);
+                        aPlugin->CompleteTask();
+                        }
+                    break;
+                default:
+                    MPX_ASSERT(0);
+                    break;
+                }
+            break;
+            }
+        case EMcsCommandExt:
+            {
+            CMPXCommand* cmd = reinterpret_cast<CMPXCommand*>(aCObject1);
+            aPlugin = reinterpret_cast<CMPXCollectionPlugin*>(aPtrData);
+            MMPXCollectionEngineObserver* callback =
+                             reinterpret_cast<MMPXCollectionEngineObserver*>(aCallback);
+            if( cmd->IsSupported( KMPXCommandGeneralId) )
+                {
+                TMPXCommandId commandId = cmd->ValueTObjectL<TMPXCommandId>(KMPXCommandGeneralId);
+                // Decrement the old plugin reference count
+                SetPlugin(EContextMedia, aPlugin);
+                aPlugin->SetCallback( callback );
+
+                if( commandId == KMPXCommandIdCollectionSelect )
+                    {
+                    // Decrement the old plugin reference count
+                    //
+                    DoHandleSelectCommandL( *cmd );
+                    aPlugin->SetCallback( NULL );
+                    aPlugin->CompleteTask();
+                    callback->HandleCommandComplete( NULL, KErrNone );
+                    }
+                else
+                    {
+                    aPlugin->SetObserver(*this);
+                    aPlugin->CommandL(*cmd);
+                    }
+                }
+            else
+                {
+                aPlugin->CompleteTask();
+                callback->HandleCommandComplete( NULL, KErrArgument );  // return error message
+                }
+            break;
+            }
+        default:
+            break;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Error happens upon request
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::HandleError(
+    CMPXCollectionPlugin& aPlugin,
+    TInt aError,
+    TBool aUnusePlugin/*=EFalse*/)
+    {
+    TInt task(aPlugin.Task());
+    MPX_DEBUG3("CMPXCollectionClientContext::HandleError %d, task %d",
+              aError, task);
+    switch (task)
+        {
+        case EMcsOpen:
+        case EMcsOpenPath:
+        case EMcsBack:
+        case EMcsOpenIndex:
+            aPlugin.Callback()->HandleOpen(iMedia, // Not used
+                                           iIndex,
+                                           ETrue, aError);
+            aPlugin.CompleteTask();
+            aPlugin.SetCallback(NULL); // Reset current observer
+            aPlugin.SetObserver(iEngine);
+            break;
+        case EMcsMediaByPath:
+            aPlugin.Callback()->HandleMedia(iMedia, aError);
+            aPlugin.CompleteTask();
+            aPlugin.SetCallback(NULL); // Reset current observer
+            aPlugin.SetObserver(iEngine);
+            break;
+        case EMcsCommandExt:
+            aPlugin.Callback()->HandleCommandComplete(iMedia, aError);
+            aPlugin.CompleteTask();
+            aPlugin.SetCallback(NULL); // Reset current observer
+            aPlugin.SetObserver(iEngine);
+            break;
+        case EMcsRemovePath:
+            {
+            CDesCArray* dummy(NULL); // object will not be dereferenced in callback
+            aPlugin.Callback()->HandleRemove(*dummy, aError);
+            aPlugin.SetCallback(NULL); // Reset current observer
+            aPlugin.SetObserver(iEngine);
+            aPlugin.CompleteTask();
+            }
+            break;
+        case EMcsFindAll:
+            aPlugin.Callback()->HandleFindAll(iMedia, aError);
+            aPlugin.SetCallback(NULL); // Reset current observer
+            aPlugin.SetObserver(iEngine);
+            aPlugin.CompleteTask();
+            break;
+        case EMcsCommand:
+            {
+            // At least complete the task to not jam up the task queue
+            aPlugin.SetCallback(NULL); // Reset current observer
+            aPlugin.SetObserver(iEngine);
+            aPlugin.CompleteTask();
+            break;
+            }
+        default:
+            break;
+        }
+    if (aUnusePlugin)
+        {
+        iEngine.ReleasePlugin(aPlugin.Uid());
+        }
+    MPX_DEBUG1("<--CMPXCollectionClientContext::HandleError");
+    }
+
+// ----------------------------------------------------------------------------
+// Error happens upon request
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::ResolvePluginL(
+    const CMPXCollectionPath& aPath,
+    TUid& aUid)
+    {
+    CMPXCollectionPlugin* p= iEngine.LoadedPlugin(aUid); // Save old plugin
+    // Resolve new plugin
+    CMPXCollectionPlugin* plugin=iEngine.ResolvePluginL(aPath);
+    // Update with new Uid
+    aUid = plugin->Uid();
+    if (p != plugin && &aPath == iBrowsePath)
+        {
+        // browsing plugin changed
+        iClientList->SendMsgL(TMPXCollectionMessage(
+                 TMPXCollectionMessage::ECollectionChanged,0,aUid.iUid));
+        }
+
+    if (p)
+        {
+        // Make sure we decrement the reference count for the old plugin
+        iEngine.ReleasePlugin(p->Uid());
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// resolve plugin for FindAllL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::ResolvePluginL(
+    const CMPXMedia& aMedia,
+    CMPXCollectionPlugin*& aPlugin)
+    {
+    // We have to be finding from the same context as browse
+    // if we are from MPX UI
+    //
+    if (iPluginUids[EContextBrowse]!=KNullUid)
+        {
+        aPlugin = LoadedPlugin(EContextBrowse);
+
+        // Increment the reference count manually
+        iEngine.UsePlugin(aPlugin->Uid());
+        }
+    else // browse context does not exist
+        {
+        if (aMedia.IsSupported(KMPXMediaGeneralCollectionId))
+            {
+            TUid col = aMedia.ValueTObjectL<TUid>(KMPXMediaGeneralCollectionId);
+
+            // Increment the plugin reference count
+            aPlugin = iEngine.ResolvePluginL(col);
+            }
+        else if (aMedia.IsSupported(KMPXMediaGeneralUri))
+            {
+            const TDesC& uri = aMedia.ValueText(KMPXMediaGeneralUri);
+
+            // Increment the plugin reference count
+            aPlugin = iEngine.ResolvePluginL(uri);
+            }
+        else
+            {
+            // Unable to find a collection plugin
+            User::Leave(KErrNotSupported);
+            }
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::SetPlugin
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::SetPlugin(
+    TContextType aType,
+    CMPXCollectionPlugin* aPlugin)
+    {
+    if (iPluginUids[aType]!=KNullUid)
+        {
+        iEngine.ReleasePlugin(iPluginUids[aType]);
+        }
+    if (aPlugin)
+        {
+        iPluginUids[aType] = aPlugin->Uid();
+        }
+    else
+        {
+        iPluginUids[aType] = KNullUid;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Add a media object to the collection
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoUpdateMediaL( TInt aOp, const CMPXMedia& aMedia )
+    {
+    // Media object can be a single "item" or a list of "items"
+    //
+    TMPXGeneralType type = EMPXNoType;
+    if (aMedia.IsSupported(KMPXMediaGeneralType))
+        {
+        type = aMedia.ValueTObjectL<TMPXGeneralType>(KMPXMediaGeneralType);
+        }
+    else
+        {
+        User::Leave(KErrArgument);
+        }
+
+    if ( type == EMPXGroup )
+        {
+        // Group has to have a "container"
+        MPX_ASSERT(aMedia.IsSupported(KMPXMediaArrayContents));
+        const CMPXMediaArray* array =
+                        aMedia.Value<CMPXMediaArray>(KMPXMediaArrayContents);
+        User::LeaveIfNull(const_cast<CMPXMediaArray*>(array));
+        TInt count = array->Count();
+        for( TInt i=0; i<count; ++i )
+            {
+            DoUpdateMediaL( aOp, *(array->AtL(i)) );
+            }
+        }
+    else if ( type == EMPXItem )
+        {
+        DoHandleItemL( aOp, aMedia );
+        }
+    else
+        {
+        // How do we handle "group" artists? 
+        User::Leave( KErrNotSupported );
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Add the media object to the correct plugin
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleItemL( TInt aOp, const CMPXMedia& aMedia)
+    {
+    MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL <---");
+
+    // Find the correct plugin.
+    // The code below increments the plugin reference count
+    CMPXCollectionPlugin* plugin(NULL);
+    if (aMedia.IsSupported(KMPXMediaGeneralCollectionId))
+        {
+        const TUid& uid = aMedia.ValueTObjectL<TUid>(KMPXMediaGeneralCollectionId);
+        MPX_DEBUG2("CMPXCollectionClientContext::DoHandleItemL Collection %i", uid.iUid);
+
+        // Increment the plugin reference count
+        plugin = iEngine.ResolvePluginL( uid );
+        }
+    else if (aMedia.IsSupported(KMPXMediaGeneralUri))
+        {
+        const TDesC& uri = aMedia.ValueText(KMPXMediaGeneralUri);
+        MPX_DEBUG2("CMPXCollectionClientContext::DoHandleItemL Collection %S", &uri);
+
+        // Increment the plugin reference count
+        plugin = iEngine.ResolvePluginL( uri );
+        }
+    else
+        {
+        MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL Cannot resolve a plugin");
+        User::Leave(KErrNotSupported);
+        }
+
+    if( !plugin )
+        {
+        MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL Cannot resolve a plugin");
+        User::Leave(KErrNotSupported);
+        }
+
+    iEngine.CleanupPluginPushL(plugin);
+
+    switch( aOp )
+        {
+        case EMcsAddItem:
+            {
+            MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL Add");
+
+            plugin->SetObserver(*this);
+            plugin->AddL( aMedia );
+            plugin->SetObserver(iEngine);
+            break;
+            }
+        case EMcsRemoveItem:
+            {
+            MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL Remove");
+            plugin->SetObserver(*this);
+            plugin->RemoveL( aMedia );
+            plugin->SetObserver(iEngine);
+            break;
+            }
+        case EMcsSetMedia:
+            {
+            MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL Set");
+            plugin->SetObserver(*this);
+            plugin->SetL( aMedia );
+            plugin->SetObserver(iEngine);
+            break;
+            }
+        default:
+            {
+            User::Leave(KErrNotSupported);
+            break;
+            }
+        }  // switch
+
+    CleanupStack::PopAndDestroy();    // plugin
+
+    MPX_DEBUG1("CMPXCollectionClientContext::DoHandleItemL --->");
+    }
+
+// ----------------------------------------------------------------------------
+// Handle a synchorouns command to the collection
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleSyncCommandL(
+    const CMPXCommand& aCmd,
+    const CMPXMessageQueue& aMsgQueue,
+    CMPXCollectionPlugin* aPlugin/*=NULL*/)
+    {
+    // Only process the command if the command id has been defined,
+    // otherwise leave with KErrArgument
+    if (aCmd.IsSupported(KMPXCommandGeneralId))
+        {
+        TMPXCommandId commandId = aCmd.ValueTObjectL<TMPXCommandId>(KMPXCommandGeneralId);
+        switch( commandId )
+            {
+            case KMPXCommandIdCollectionSelect:
+                {
+                DoHandleSelectCommandL( aCmd );
+                break;
+                }
+
+            case KMPXCommandSubscriptionAdd:
+                {
+                TInt index( iClientList->Find( aMsgQueue ));
+                CMPXMediaArray* items(
+                    aCmd.Value<CMPXMediaArray>( KMPXCommandSubscriptionAddItems ));
+                User::LeaveIfNull(items);
+                CMPXSubscription* subscription( CMPXSubscription::NewL( *items ));
+                 CleanupStack::PushL(subscription);
+                 iClientList->AddSubscriptionL( index, subscription );   // ownership transferred
+                 CleanupStack::Pop(subscription);
+                break;
+                }
+            case KMPXCommandSubscriptionRemove:
+                {
+                TInt index( iClientList->Find( aMsgQueue ));
+                CMPXMediaArray* items(
+                    aCmd.Value<CMPXMediaArray>( KMPXCommandSubscriptionAddItems ));
+                User::LeaveIfNull(items);
+                CMPXSubscription* subscription( CMPXSubscription::NewL( *items ));
+                 CleanupStack::PushL(subscription);
+                 iClientList->RemoveSubscriptionL( index, *subscription );
+                 CleanupStack::PopAndDestroy(subscription);
+                break;
+                }
+            case KMPXCommandSubscriptionRemoveAll:
+                {
+                TInt index( iClientList->Find( aMsgQueue ));
+                 iClientList->RemoveAllSubscriptionsL( index );
+                break;
+                }
+                
+           case KMPXCommandIdCollectionPrepareDelete:
+                {
+                if (aCmd.IsSupported (KMPXCommandCollectionPrepareRemovePath))
+                    {
+                    CMPXCollectionPath
+                            * path = aCmd.ValueCObjectL<CMPXCollectionPath> (KMPXCommandCollectionPrepareRemovePath);
+                    CleanupStack::PushL(path);
+                    iEngine.ResetCacheL(*path);
+                    CleanupStack::PopAndDestroy(path);
+                    }
+                break;
+                }
+
+            default:
+                {
+                MPX_ASSERT(aPlugin);
+                aPlugin->SetObserver(*this);
+                TRAPD( err, aPlugin->CommandL( const_cast<CMPXCommand&>(aCmd) ) );
+                aPlugin->SetObserver(iEngine);
+                User::LeaveIfError( err );
+                break;
+                }
+            }
+        }
+    else
+        {
+        User::Leave(KErrArgument);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::DoHandleSelectCommandL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoHandleSelectCommandL(const CMPXCommand& aCmd)
+    {
+    TInt index(iIndex);
+
+    // Selecting a new index
+    if( aCmd.IsSupported(KMPXCommandCollectionSelectIndex) )
+        {
+        index = aCmd.ValueTObjectL<TInt>(KMPXCommandCollectionSelectIndex);
+        if (iBrowsePath->Levels()<=0)
+            {
+            User::Leave(KErrNotReady);
+            }
+        if (index<0 || index>=iBrowsePath->Count())
+            {
+            User::Leave(KErrArgument);
+            }
+        iBrowsePath->Set(index);
+        }
+    // Re-select the current index
+    else
+        {
+        TInt count = iBrowsePath->Count();
+
+        if( iFocusItemId != KMPXInvalidItemId )
+            {
+            TInt index = iBrowsePath->IndexOfId(iFocusItemId);
+            if (KErrNotFound != index)
+                {
+                iBrowsePath->Set(index);
+                }
+            else if( count > 0 ) // Focus item has been deleted, select next
+                {
+                // Bounds check for largest and smallest
+                if( iIndex >= count )
+                    {
+                    iIndex = count-1;
+                    }
+                if( iIndex < 0 )
+                    {
+                    iIndex = 0;
+                    }
+                iBrowsePath->Set(iIndex);
+                iFocusItemId = iBrowsePath->Id();
+                }
+            iIndex = iBrowsePath->Index();
+            }
+        else
+            {
+            // Bounds checking for iIndex
+            if( iIndex >= count && iIndex > 0 )
+                {
+                iIndex = count-1;
+
+                // Just in case if ids has 0 items
+                if( iIndex > 0 && iIndex < count )
+                    {
+                    iFocusItemId = iBrowsePath->IdOfIndex(iIndex);
+                    }
+                }
+
+            if (iBrowsePath->Levels()>0 && iIndex>=0 &&
+                iIndex<iBrowsePath->Count())
+                {
+                iBrowsePath->Set(iIndex);
+                }
+            iFocusItemId = iBrowsePath->Id();
+            }
+        }
+    iClientList->SendMsgL(
+          TMPXCollectionMessage(TMPXCollectionMessage::EFocusChanged,
+                                index, index));
+    iIndex = index; // iIndex will always be up to date
+    iFocusItemId = iBrowsePath->Id();
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::DoPluginOpenL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoPluginOpenL()
+    {
+    MPX_FUNC("CMPXCollectionClientContext::DoPluginOpenL()");
+    TBool callOpen(ETrue);
+    MPX_ASSERT(iPluginUids[EContextBrowse]!=KNullUid);
+    TBool pluginCacheable(EFalse);
+    if( iBrowsePath )
+        {
+        TMPXItemId id(iBrowsePath->Id(CMPXCollectionPath::ECollectionUid) );
+        pluginCacheable = iEngine.PluginCacheable( TUid::Uid( id ) );
+        }
+
+    // Check for open playlist only mode.  If in that mode, do not return the media
+    // from the cache but call the plugin to open, as that will callback a
+    // different HandleOpenL() with the collection path instead.
+    TMPXOpenMode mode( iBrowsePath->OpenNextMode() );
+    CMPXCollectionPlugin* plugin = LoadedPlugin(EContextBrowse);
+    if ( !iFilter && mode != EMPXOpenPlaylistOnly )
+        {
+        // try to get the results from the cache only if the plugin is cacheable
+        if ( pluginCacheable )
+            {
+            CMPXMedia* results( iCache.GetL( *iBrowsePath,
+                                             iBrowsePath->OpenAttributes(),
+                                             ETrue ));
+            if (results)
+                {
+                MPX_DEBUG1("CMPXCollectionClientContext::DoPluginOpenL(): Results found in cache");
+                MMPXCollectionEngineObserver* callback = plugin->Callback();
+                plugin->CompleteTask();
+                TRAPD(err, DoHandleOpenL(results, NULL, callback, KErrNone, EFalse, ETrue));
+                if (err)
+                    {
+                    HandleError(*plugin, err);
+                    }
+
+                callOpen = EFalse;
+                }
+            }
+        }
+
+    if (callOpen)
+        {
+        if ( pluginCacheable )
+            {
+            iCacheMedia = AttributesCacheableL( iBrowsePath->OpenAttributes(), *iBrowsePath );
+            }
+        plugin->OpenL(*iBrowsePath, iBrowsePath->OpenAttributes(), iFilter);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::DoPluginMediaL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionClientContext::DoPluginMediaL(
+    CMPXCollectionPath& aPath,
+    const TCapabilitySet& aCaps,
+    CMPXAttributeSpecs& aSpecs,
+    CMPXFilter& aFilter)
+    {
+    MPX_FUNC("CMPXCollectionClientContext::DoPluginMediaL()");
+    TBool callMedia(ETrue);
+    delete iMediaPath;
+    iMediaPath = NULL;
+
+    TMPXItemId id(aPath.Id(CMPXCollectionPath::ECollectionUid) );
+    TBool pluginCacheable( iEngine.PluginCacheable( TUid::Uid( id ) ));
+
+    // try to get the results from the cache only if
+    // the plugin is cacheable
+    CMPXMedia* results( NULL );
+    if ( pluginCacheable )
+        {
+        results = iCache.GetL( aPath, aPath.OpenAttributes() );
+
+        if (results)
+            {
+            // Check if the attribute specs match
+            if ( aSpecs.Count() > 0 )
+                {
+                /*
+                // need to check if requested attribute specs
+                // match what we have cached
+                if ( results->IsSupported( KMPXCommandMediaAttributeSpecs ))
+                    {
+                    CMPXAttributeSpecs* specs( results->Value<CMPXAttributeSpecs>(
+                                                KMPXCommandMediaAttributeSpecs ));
+                    User::LeaveIfNull(specs);
+                    if ( *specs == *aSpecs )
+                        {
+                        callMedia = EFalse;
+                        }
+                    }
+                */
+                }
+            else
+                {
+                // Else client did not specify attribute specs, so we can return
+                // the match
+                callMedia = EFalse;
+                }
+            }
+        }
+
+    if ( callMedia )
+        {
+        iMediaPath = CMPXCollectionPath::NewL( aPath );
+
+        if ( pluginCacheable )
+            {
+            iCacheMedia = AttributesCacheableL( aPath.OpenAttributes(), aPath );
+            }
+        LoadedPlugin(EContextMedia)->MediaL( aPath,
+                                             aPath.OpenAttributes(),
+                                             aCaps,
+                                             &aSpecs,
+                                             &aFilter);
+        }
+    else
+        {
+        HandleMedia( results, KErrNone );
+        }
+    }
+
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::AttributesCacheableL
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionClientContext::AttributesCacheableL(
+    const TArray<TMPXAttribute>& aAttrs,
+    const CMPXCollectionPath& aPath )
+    {
+    MPX_DEBUG1("-->CMPXCollectionClientContext::AttributesCacheableL");
+
+    TMPXItemId id( aPath.Id( CMPXCollectionPath::ECollectionUid ));
+    const TArray<TUid>& nonCacheAttrs( iEngine.PluginNonCacheableAttributesL( TUid::Uid( id )));
+
+    TBool found( EFalse );
+    TInt nonCacheCount( nonCacheAttrs.Count() );
+    for ( TInt i = 0; i < nonCacheCount && !found; i++ )
+        {
+        TInt attrCount( aAttrs.Count() );
+        for ( TInt j = 0; j < attrCount && !found; j++ )
+            {
+            TMPXAttribute att( aAttrs[j] );
+            if ( nonCacheAttrs[i] == TUid::Uid( att.ContentId() ))
+                {
+                found = ETrue;
+                }
+            }
+        }
+    MPX_DEBUG1("<--CMPXCollectionClientContext::AttributesCacheableL");
+    return !found;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::AddToCache
+// ----------------------------------------------------------------------------
+//
+CMPXMedia* CMPXCollectionClientContext::AddToCache(
+    const CMPXCollectionPath& aPath,
+    const TArray<TMPXAttribute>& aAttrs,
+    CMPXMedia& aResults,
+    TBool aMediaFromOpenL,
+    CMPXCollectionCache::TCachePriority aPriority /* = EPriorityNormal */)
+    {
+    CMPXMedia* ret( &aResults );
+    TRAP_IGNORE(ret = iCache.AddL(aPath, aAttrs, aResults, aMediaFromOpenL, aPriority));
+    return ret;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionClientContext::LoadedPlugin
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionPlugin* CMPXCollectionClientContext::LoadedPlugin(TContextType aType)
+    {
+    MPX_ASSERT(iPluginUids[aType]!=KNullUid);
+    CMPXCollectionPlugin* plugin(iEngine.LoadedPlugin(iPluginUids[aType]));
+    MPX_ASSERT(plugin);
+    return plugin;
+    }
+
+// End of file