From 5b23b6ad810d341e8b4b4047f508d9c3bd6ce1e4 Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 2 Dec 2010 22:15:43 -0500 Subject: [PATCH] first saving non-working commit --- include/tomahawk/playlist.h | 15 +- include/tomahawk/typedefs.h | 2 + src/CMakeLists.txt | 10 ++ src/dynamic/dynamiccontrol.cpp | 0 src/dynamic/dynamiccontrol.h | 6 + src/dynamic/dynamicplaylist.cpp | 238 +++++++++++++++++++++++++++++++ src/dynamic/dynamicplaylist.h | 120 ++++++++++++++++ src/dynamic/generatorfactory.cpp | 23 +++ src/dynamic/generatorfactory.h | 39 +++++ src/dynamic/generatorinterface.h | 52 +++++++ src/playlist.cpp | 201 ++++++++++++++------------ 11 files changed, 609 insertions(+), 97 deletions(-) create mode 100644 src/dynamic/dynamiccontrol.cpp create mode 100644 src/dynamic/dynamiccontrol.h create mode 100644 src/dynamic/dynamicplaylist.cpp create mode 100644 src/dynamic/dynamicplaylist.h create mode 100644 src/dynamic/generatorfactory.cpp create mode 100644 src/dynamic/generatorfactory.h create mode 100644 src/dynamic/generatorinterface.h diff --git a/include/tomahawk/playlist.h b/include/tomahawk/playlist.h index 92ae6d280..bef3cc76f 100644 --- a/include/tomahawk/playlist.h +++ b/include/tomahawk/playlist.h @@ -118,7 +118,7 @@ public: // these need to exist and be public for the json serialization stuff // you SHOULD NOT call them. They are used for an alternate CTOR method from json. // maybe friend QObjectHelper and make them private? - Playlist( const source_ptr& author ) + explicit Playlist( const source_ptr& author ) : m_source( author ) , m_lastmodified( 0 ) { @@ -155,7 +155,7 @@ public slots: void resolve(); -private: +protected: // called from loadAllPlaylists DB cmd: explicit Playlist( const source_ptr& src, const QString& currentrevision, @@ -173,9 +173,14 @@ private: const QString& info, const QString& creator, bool shared ); - - void rundb(); - + + QList newEntries( const QList< plentry_ptr >& entries ); + PlaylistRevision setNewRevision( const QString& rev, + const QList& neworderedguids, + const QList& oldorderedguids, + bool is_newest_rev, + const QMap< QString, Tomahawk::plentry_ptr >& addedmap ); +private: source_ptr m_source; QString m_currentrevision; QString m_guid, m_title, m_info, m_creator; diff --git a/include/tomahawk/typedefs.h b/include/tomahawk/typedefs.h index 0140306bd..9e47a9d55 100644 --- a/include/tomahawk/typedefs.h +++ b/include/tomahawk/typedefs.h @@ -11,6 +11,7 @@ namespace Tomahawk class Collection; class Playlist; class PlaylistEntry; + class DynamicPlaylist; class Query; class Result; class Source; @@ -18,6 +19,7 @@ namespace Tomahawk typedef QSharedPointer collection_ptr; typedef QSharedPointer playlist_ptr; typedef QSharedPointer plentry_ptr; + typedef QSharedPointer dynplaylist_ptr; typedef QSharedPointer query_ptr; typedef QSharedPointer result_ptr; typedef QSharedPointer source_ptr; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9697983e3..8b2aa5b32 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,10 @@ SET( tomahawkSources ${tomahawkSources} database/databasecommand_updatesearchindex.cpp database/databasecollection.cpp + dynamic/dynamicplaylist.cpp + dynamic/dynamiccontrol.cpp + dynamic/generatorfactory.cpp + scrobbler.cpp xmppbot/xmppbot.cpp web/api_v1.cpp @@ -222,6 +226,11 @@ SET( tomahawkHeaders ${tomahawkHeaders} network/filetransferconnection.h network/dbsyncconnection.h + dynamic/dynamicplaylist.h + dynamic/dynamiccontrol.h + dynamic/generatorfactory.h + dynamic/generatorinterface.h + musicscanner.h scriptresolver.h tomahawksettings.h @@ -308,6 +317,7 @@ INCLUDE_DIRECTORIES( sourcetree topbar utils + dynamic ../rtaudio ../alsa-playback diff --git a/src/dynamic/dynamiccontrol.cpp b/src/dynamic/dynamiccontrol.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/dynamic/dynamiccontrol.h b/src/dynamic/dynamiccontrol.h new file mode 100644 index 000000000..c517f289e --- /dev/null +++ b/src/dynamic/dynamiccontrol.h @@ -0,0 +1,6 @@ +#ifndef DYNAMIC_PLAYLIST_CONTROL +#define DYNAMIC_PLAYLIST_CONTROL + + + +#endif diff --git a/src/dynamic/dynamicplaylist.cpp b/src/dynamic/dynamicplaylist.cpp new file mode 100644 index 000000000..6ac880e84 --- /dev/null +++ b/src/dynamic/dynamicplaylist.cpp @@ -0,0 +1,238 @@ +#include "dynamicplaylist.h" + +#include "tomahawk/tomahawkapp.h" + +using namespace Tomahawk; + +// Called by loadAllPlaylists command +DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& src, + const QString& currentrevision, + const QString& title, + const QString& info, + const QString& creator, + const QString& type, + bool shared, + int lastmod, + const QString& guid ) + : Playlist( src, currentrevision, title, info, creator, shared, lastmod, guid ) +{ + qDebug() << "Creating Dynamic Playlist 1"; + // TODO instantiate generator +} + + +// called when a new playlist is created (no currentrevision, new guid) +DynamicPlaylist::DynamicPlaylist ( const Tomahawk::source_ptr& author, + const QString& guid, + const QString& title, + const QString& info, + const QString& creator, + const QString& type, + bool shared ) + : Playlist ( author, guid, title, info, creator, shared ) +{ + qDebug() << "Creating Dynamic Playlist 2"; + // TODO instantiate generator +} + +dynplaylist_ptr DynamicPlaylist::create( const Tomahawk::source_ptr& author, + const QString& guid, + const QString& title, + const QString& info, + const QString& creator, + bool shared ) +{ + // TODO default generator? + QString type = "default_generator"; + dynplaylist_ptr dynplaylist = new dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, shared ) ); + + DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist ); + connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); + APP->database()->enqueue( QSharedPointer(cmd) ); + dynplaylist->reportCreated( dynplaylist ); + return dynplaylist; + +} + +// create a new revision that will be a static playlist, as it has entries +void DynamicPlaylist::createNewRevision( const QString& newrev, + const QString& oldrev, + const QString& type, + const QList< dyncontrol_ptr>& controls, + const QList< plentry_ptr >& entries ) +{ + // get the newly added tracks + QList< plentry_ptr > added = newEntries( entries ); + + QStringList orderedguids; + foreach( plentry_ptr p, entries ) + orderedguids << p->guid(); + + // no conflict resolution or partial updating for controls. all or nothing baby + + // source making the change (localy user in this case) + source_ptr author = APP->sourcelist().getLocal(); + // command writes new rev to DB and calls setRevision, which emits our signal + DatabaseCommand_SetDynamicPlaylistRevision* cmd = + new DatabaseCommand_SetDynamicPlaylistRevision( author, + guid(), + newrev, + oldrev, + orderedguids, + added, + type, + STATIC, + controls ); + APP->database()->enqueue( QSharedPointer( cmd ) ); +} + +// create a new revision that will be an ondemand playlist, as it has no entries +void DynamicPlaylist::createNewRevision( const QString& newrev, + const QString& oldrev, + const QString& type, + const QList< dyncontrol_ptr>& controls ) +{ + // can skip the entry stuff. just overwrite with new info + source_ptr author = APP->sourcelist().getLocal(); + // command writes new rev to DB and calls setRevision, which emits our signal + DatabaseCommand_SetDynamicPlaylistRevision* cmd = + new DatabaseCommand_SetDynamicPlaylistRevision( author, + guid(), + newrev, + oldrev, + type, + STATIC, + controls ); + APP->database()->enqueue( QSharedPointer( cmd ) ); +} + +void DynamicPlaylist::loadRevision( const QString& rev ) +{ + qDebug() << Q_FUNC_INFO; + + DatabaseCommand_LoadDynamicPlaylist* cmd = + new DatabaseCommand_LoadDynamicPlaylist( rev.isEmpty() ? currentrevision() : rev, m_generator->mode() ); + + if( m_generator->mode() == ONDEMAND ) { + connect( cmd, SIGNAL( done( QString, + bool, + const QString, + QList< dyncontrol_ptr>, + bool ) ), + SLOT( setRevision( QString, + bool, + const QString, + QList< dyncontrol_ptr>, + bool) ) ); + } else if( m_generator->mode() == STATIC ) { + connect( cmd, SIGNAL( done( QString, + QList< QString >, + QList< QString >, + QString, + QList< dyncontrol_ptr>, + bool, + const QMap< QString, Tomahawk::plentry_ptr >, + bool ) ), + SLOT( setRevision( QString, + QList< QString >, + QList< QString >, + QString, + QList< dyncontrol_ptr>, + bool, + const QMap< QString, Tomahawk::plentry_ptr >, + bool ) ) ); + + } + APP->database()->enqueue( QSharedPointer( cmd ) ); +} + +bool DynamicPlaylist::remove( const Tomahawk::dynplaylist_ptr& playlist ) +{ + return remove( playlist.staticCast() ); +} + +void DynamicPlaylist::reportCreated( const Tomahawk::dynplaylist_ptr& self ) +{ + qDebug() << Q_FUNC_INFO; + Q_ASSERT( self.data() == this ); + // will emit Collection::playlistCreated(...) + m_source->collection()->addPlaylist( self.staticCast() ); +} + +void DynamicPlaylist::reportDeleted( const Tomahawk::dynplaylist_ptr& self ) +{ + qDebug() << Q_FUNC_INFO; + Q_ASSERT( self.data() == this ); + // will emit Collection::playlistCreated(...) + m_source->collection()->deletePlaylist( self.staticCast() ); +} + +// static version +void DynamicPlaylist::setRevision( const QString& rev, + const QList< QString >& neworderedguids, + const QList< QString >& oldorderedguids, + const QString& type, + const QList< dyncontrol_ptr>& controls, + bool is_newest_rev, + const QMap< QString, plentry_ptr >& addedmap, + bool applied ) +{ + // we're probably being called by a database worker thread + if( QThread::currentThread() != thread() ) + { + QMetaObject::invokeMethod( this, + "setRevision", + Qt::BlockingQueuedConnection, + Q_ARG( QString, rev ), + Q_ARG( QList, neworderedguids ), + Q_ARG( QList, oldorderedguids ), + Q_ARG( QString, type ), + QGenericArgument( "QList< dyncontrol_ptr >" , (const void*)&controls ), + Q_ARG( bool, is_newest_rev ), + QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr >" , (const void*)&addedmap ), + Q_ARG( bool, applied ) ); + return; + } + if( m_generator->type() != type ) { // new generator needed + m_generator = generatorinterface_ptr( GeneratorFactory::create( type ) ); + } + + m_generator->setControls( controls ); + m_generator->setMode( ONDEMAND ) + + DynamicPlaylistRevision pr = DynamicPlaylistRevision( setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap ) ); + pr.controls = controls; + pr.type = type; + pr.mode = STATIC; + + if( applied ) + setCurrentRevision( rev ); + pr.applied = applied; + + emit revisionLoaded( pr ); +} + +// ondemand version +void DynamicPlaylist::setRevision( const QString& rev, + bool is_newest_rev, + const QString& type, + const QList< dyncontrol_ptr>& controls, + bool applied ) +{ + if( m_generator->type() != type ) { // new generator needed + m_generator = generatorinterface_ptr( GeneratorFactory::create( type ) ); + } + + m_generator->setControls( controls ); + m_generator->setMode( ONDEMAND ) + + DynamicPlaylistRevision pr; + pr.oldrevisionguid = m_currentrevision; + pr.revisionguid = rev; + pr.controls = controls; + pr.type = type; + pr.mode = ONDEMAND; + + emit revisionLoaded( pr ); +} + diff --git a/src/dynamic/dynamicplaylist.h b/src/dynamic/dynamicplaylist.h new file mode 100644 index 000000000..6996b972d --- /dev/null +++ b/src/dynamic/dynamicplaylist.h @@ -0,0 +1,120 @@ +#ifndef DYNAMIC_PLAYLIST_H +#define DYNAMIC_PLAYLIST_H + +#include +#include +#include + +#include "tomahawk/playlist.h" +#include "dynamic/generatorinterface.h" + +namespace Tomahawk { + +/** + * Subclass of playlist that adds the information needed to store a dynamic playlist. + * It uses normal PlaylistEntries but also has a mode, a generator, and a list of controls +*/ + +class DynamicPlaylist : public Playlist +{ + Q_OBJECT + + Q_PROPERTY( GeneratorMode mode WRITE setMode READ mode ) + Q_PROPERTY( QString type WRITE setType READ type ) + +public: + enum DynamicPlaylistMode { + OnDemand = 0, + StaticPlaylist + }; + + /// Generate an empty dynamic playlist with default generator + static Tomahawk::dynplaylist_ptr create( const source_ptr& author, + const QString& guid, + const QString& title, + const QString& info, + const QString& creator, + bool shared + ); + static bool remove( const dynplaylist_ptr& playlist ); + + virtual void loadRevision( const QString& rev = "" ); + + GeneratorMode mode() const { return m_generator->mode(); } + QString type() const { return m_generator->type(); } + generatorinterface_ptr generator() const { return m_generator; } + + // + // these need to exist and be public for the json serialization stuff + // you SHOULD NOT call them. They are used for an alternate CTOR method from json. + // maybe friend QObjectHelper and make them private? + explicit DynamicPlaylist( const source_ptr& author ) + : Playlist( author ) + { + qDebug() << Q_FUNC_INFO << "JSON"; + } + void setMode( GeneratorMode mode ) { m_generator->setMode( mode ); } + void setType( const QString& type ) { /** TODO */; } + void setGenerator( const generatorinterface_ptr& gen_ptr ) { m_generator = gen_ptr; } + // + +signals: + /// emitted when the playlist revision changes (whenever the playlist changes) + void revisionLoaded( Tomahawk::DynamicPlaylistRevision ); + +public slots: + // want to update the playlist from the model? + // generate a newrev using uuid() and call this: + // if this is a static playlist, pass it a new list of entries. implicitly sets mode to static + void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls, const QList< plentry_ptr >& entries ); + // if it is ondemand, no entries are needed implicitly sets mode to ondemand + void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls ); + + void reportCreated( const Tomahawk::dynplaylist_ptr& self ); + void reportDeleted( const Tomahawk::dynplaylist_ptr& self ); + + // called from setdynamicplaylistrevision db cmd + // static version + void setRevision( const QString& rev, + const QList& neworderedguids, + const QList& oldorderedguids, + const QString& type, + const QList< dyncontrol_ptr>& controls, + bool is_newest_rev, + const QMap< QString, Tomahawk::plentry_ptr >& addedmap, + bool applied ); + + // ondemand version + void setRevision( const QString& rev, + bool is_newest_rev, + const QString& type, + const QList< dyncontrol_ptr>& controls, + bool applied ); +private: + // called from loadAllPlaylists DB cmd: + explicit DynamicPlaylist( const source_ptr& src, + const QString& currentrevision, + const QString& title, + const QString& info, + const QString& creator, + const QString& type, + bool shared, + int lastmod, + const QString& guid = "" ); // populate db + + // called when creating new playlist + explicit DynamicPlaylist( const source_ptr& author, + const QString& guid, + const QString& title, + const QString& info, + const QString& creator, + const QString& type, + bool shared ); + +private: + generatorinterface_ptr m_generator; +}; + +}; // namespace + +#endif \ No newline at end of file diff --git a/src/dynamic/generatorfactory.cpp b/src/dynamic/generatorfactory.cpp new file mode 100644 index 000000000..c9b0ddd16 --- /dev/null +++ b/src/dynamic/generatorfactory.cpp @@ -0,0 +1,23 @@ +#include "generatorfactory.h" + +Tomahawk::GeneratorFactory::GeneratorFactory() +{ +} + +Tomahawk::GeneratorFactory::~GeneratorFactory() +{ + qDeleteAll( m_factories.values() ); +} + +generatorinterface_ptr Tomahawk::GeneratorFactory::create ( const QString& type ) +{ + if( !m_factories.contains( type ) ) + return generatorinterface_ptr(); + + return generatorinterface_ptr( m_factories.value( type )->create() ); +} + +void Tomahawk::GeneratorFactory::registerFactory ( const QString& type, Tomahawk::GeneratorFactoryInterface* interface ) +{ + m_factories.insert( type, interface ); +} diff --git a/src/dynamic/generatorfactory.h b/src/dynamic/generatorfactory.h new file mode 100644 index 000000000..8c68f112e --- /dev/null +++ b/src/dynamic/generatorfactory.h @@ -0,0 +1,39 @@ +#ifndef GENERATOR_FACTORY_H +#define GENERATOR_FACTORY_H + +#include +#include + +namespace Tomahawk { + +/** + * Generators should subclass this and have it create the custom Generator + */ +class GeneratorFactoryInterface +{ +public: + GeneratorInterface* create() = 0; +}; + +/** + * Simple factory that generates Generators from string type descriptors + */ +class GeneratorFactory +{ +public: + GeneratorFactory(); + ~GeneratorFactory(); + + generatorinterface_ptr create( const QString& type ); + + void registerFactory( const QString& type, GeneratorFactoryInterface* interface ); + +private: + QHash m_factories; + +}; + + +}; + +#endif diff --git a/src/dynamic/generatorinterface.h b/src/dynamic/generatorinterface.h new file mode 100644 index 000000000..3823f649f --- /dev/null +++ b/src/dynamic/generatorinterface.h @@ -0,0 +1,52 @@ +#ifndef GENERATOR_INTERFACE_H +#define GENERATOR_INTERFACE_H + +#include + +namespace Tomahawk { + +enum GeneratorMode { + OnDemand = 0, + Static +}; + +/** + * The abstract interface for Dynamic Playlist Generators. Generators have the following features: + * - They create new DynamicControls that are appropriate for the generator + * - They expose a list of controls that this generator currently is operating on + * - They have a state of OnDemand or Static + */ +class GeneratorInterface : public QObject +{ + Q_OBJECT + Q_PROPERTY( QString type READ type ) + Q_PROPERTY( GeneratorMode mode READ mode WRITE setMode ); + Q_ENUMS( GeneratorMode ) + +public: + explicit GeneratorInterface() {} + virtual ~GeneratorInterface() {} + + // Can't make it pure otherwise we can't shove it in QVariants :-/ + virtual dyncontrol_ptr createControl() const {} + + /// The type of this generator + virtual QString type() const { return m_type; } + + virtual GeneratorMode mode() const { return m_mode; } + virtual void setMode( GeneratorMode mode ) { m_mode = mode; } + + // control functions + virtual QList< dyncontrol_ptr > controls() const { return m_controls; } + virtual void addControl( const dyncontrol_ptr& control ) { m_controls << control; } + virtual void clearControls() { m_controls.clear(); } + virtual void setControls( const QList< dyncontrol_ptr>& controls ) { m_controls = controls; } +private: + QString m_type; + GeneratorMode m_mode; + QList< dyncontrol_ptr > m_controls; +}; + +}; + +#endif diff --git a/src/playlist.cpp b/src/playlist.cpp index 824e34c48..0e47de8a2 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -168,21 +168,11 @@ void Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries ) { // qDebug() << "m_entries guids:"; - // foreach( plentry_ptr pp, m_entries ) qDebug() << pp->guid(); - - QSet currentguids; - foreach( plentry_ptr p, m_entries ) - currentguids.insert( p->guid() ); // could be cached as member? - // calc list of newly added entries: - QList added; + QList added = newEntries( entries ); QStringList orderedguids; foreach( plentry_ptr p, entries ) - { orderedguids << p->guid(); - if( !currentguids.contains(p->guid()) ) - added << p; - } // source making the change (localy user in this case) source_ptr author = APP->sourcelist().getLocal(); @@ -225,87 +215,9 @@ Playlist::setRevision( const QString& rev, ); return; } - //qDebug() << Q_FUNC_INFO << (qlonglong)this - // << rev << neworderedguids << oldorderedguids - // << "isnewest:" << is_newest_rev << addedmap << applied << m_entries - // ; - - // build up correctly ordered new list of plentry_ptrs from - // existing ones, and the ones that have been added - QMap entriesmap; - foreach( const plentry_ptr& p, m_entries ) - entriesmap.insert( p->guid(), p ); - - //qDebug() << "Entries map:" << entriesmap; - - QList entries; - //qDebug() << "m_entries:" << m_entries.count() << m_entries; - - //qDebug() << "counters:" << neworderedguids.count() << entriesmap.count() << addedmap.count(); - foreach( const QString& id, neworderedguids ) - { - //qDebug() << "id:" << id; - //qDebug() << "newordered:" << neworderedguids.count() << neworderedguids; - //qDebug() << "entriesmap:" << entriesmap.count() << entriesmap; - //qDebug() << "addedmap:" << addedmap.count() << addedmap; - //qDebug() << "m_entries" << m_entries; - - if( entriesmap.contains( id ) ) - { - entries.append( entriesmap.value( id ) ); - } - else if( addedmap.contains( id ) ) - { - entries.append( addedmap.value( id ) ); - if( is_newest_rev ) m_entries.append( addedmap.value( id ) ); - } - else - { - Q_ASSERT( false ); // XXX - } - } - - //qDebug() << Q_FUNC_INFO << rev << entries.length() << applied; - - PlaylistRevision pr; - pr.oldrevisionguid = m_currentrevision; - pr.revisionguid = rev; - - // entries that have been removed: - QSet removedguids = oldorderedguids.toSet().subtract( neworderedguids.toSet() ); - //qDebug() << "Removedguids:" << removedguids << "oldorederedguids" << oldorderedguids << "newog" << neworderedguids; - foreach( QString remid, removedguids ) - { - // NB: entriesmap will contain old/removed entries only if the removal was done - // in the same session - after a restart, history is not in memory. - if( entriesmap.contains( remid ) ) - { - pr.removed << entriesmap.value( remid ); - if( is_newest_rev ) - { - //qDebug() << "Removing from m_entries" << remid; - for( int k = 0 ; kguid() == remid ) - { - //qDebug() << "removed at " << k; - m_entries.removeAt(k); - break; - } - } - } - } - } - - pr.added = addedmap.values(); - - //qDebug() << "Revision set:" << rev - // << "added" << pr.added.size() - // << "removed" << pr.removed.size() - // << "total entries" << m_entries.size(); - - pr.newlist = entries; - + + PlaylistRevision pr = setNewRevision( rev, neworderedguids, oldorderedguids, is_newest_rev, addedmap ); + if( applied ) m_currentrevision = rev; pr.applied = applied; @@ -313,6 +225,94 @@ Playlist::setRevision( const QString& rev, emit revisionLoaded( pr ); } +PlaylistRevision +Playlist::setNewRevision( const QString& rev, + const QList& neworderedguids, + const QList& oldorderedguids, + bool is_newest_rev, + const QMap< QString, Tomahawk::plentry_ptr >& addedmap ) +{ + //qDebug() << Q_FUNC_INFO << (qlonglong)this + // << rev << neworderedguids << oldorderedguids + // << "isnewest:" << is_newest_rev << addedmap << applied << m_entries + // ; + + // build up correctly ordered new list of plentry_ptrs from + // existing ones, and the ones that have been added + QMap entriesmap; + foreach( const plentry_ptr& p, m_entries ) + entriesmap.insert( p->guid(), p ); + + //qDebug() << "Entries map:" << entriesmap; + + QList entries; + //qDebug() << "m_entries:" << m_entries.count() << m_entries; + + //qDebug() << "counters:" << neworderedguids.count() << entriesmap.count() << addedmap.count(); + foreach( const QString& id, neworderedguids ) + { + //qDebug() << "id:" << id; + //qDebug() << "newordered:" << neworderedguids.count() << neworderedguids; + //qDebug() << "entriesmap:" << entriesmap.count() << entriesmap; + //qDebug() << "addedmap:" << addedmap.count() << addedmap; + //qDebug() << "m_entries" << m_entries; + + if( entriesmap.contains( id ) ) + { + entries.append( entriesmap.value( id ) ); + } + else if( addedmap.contains( id ) ) + { + entries.append( addedmap.value( id ) ); + if( is_newest_rev ) m_entries.append( addedmap.value( id ) ); + } + else + { + Q_ASSERT( false ); // XXX + } + } + + //qDebug() << Q_FUNC_INFO << rev << entries.length() << applied; + + PlaylistRevision pr; + pr.oldrevisionguid = m_currentrevision; + pr.revisionguid = rev; + + // entries that have been removed: + QSet removedguids = oldorderedguids.toSet().subtract( neworderedguids.toSet() ); + //qDebug() << "Removedguids:" << removedguids << "oldorederedguids" << oldorderedguids << "newog" << neworderedguids; + foreach( QString remid, removedguids ) + { + // NB: entriesmap will contain old/removed entries only if the removal was done + // in the same session - after a restart, history is not in memory. + if( entriesmap.contains( remid ) ) + { + pr.removed << entriesmap.value( remid ); + if( is_newest_rev ) + { + //qDebug() << "Removing from m_entries" << remid; + for( int k = 0 ; kguid() == remid ) + { + //qDebug() << "removed at " << k; + m_entries.removeAt(k); + break; + } + } + } + } + } + + pr.added = addedmap.values(); + + //qDebug() << "Revision set:" << rev + // << "added" << pr.added.size() + // << "removed" << pr.removed.size() + // << "total entries" << m_entries.size(); + + pr.newlist = entries; +} void Playlist::resolve() { @@ -361,3 +361,20 @@ Playlist::addEntries( const QList& queries, const QString& oldrev ) QString newrev = uuid(); createNewRevision( newrev, oldrev, el ); } + +QList< plentry_ptr > +Playlist::newEntries( const QList< plentry_ptr > entries ) +{ + QSet currentguids; + foreach( plentry_ptr p, m_entries ) + currentguids.insert( p->guid() ); // could be cached as member? + + // calc list of newly added entries: + QList added; + foreach( plentry_ptr p, entries ) + { + if( !currentguids.contains(p->guid()) ) + added << p; + } + return added; +}