diff --git a/src/accounts/spotify/SpotifyAccount.cpp b/src/accounts/spotify/SpotifyAccount.cpp index 554551633..20273f2cc 100644 --- a/src/accounts/spotify/SpotifyAccount.cpp +++ b/src/accounts/spotify/SpotifyAccount.cpp @@ -119,8 +119,6 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg setConfiguration( config ); sync(); - qDebug() << "SET SPOTIFY CREDS:" << credentials().value( "username" ) << credentials().value( "password" ); - return; } @@ -128,10 +126,11 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg const QString qid = msg.value( "qid" ).toString(); if ( m_qidToSlotMap.contains( qid ) ) { - const QString& slot = m_qidToSlotMap[ qid ]; + QObject* receiver = m_qidToSlotMap[ qid ].first; + const QString& slot = m_qidToSlotMap[ qid ].second; m_qidToSlotMap.remove( qid ); - QMetaObject::invokeMethod( this, slot.toLatin1(), Q_ARG( QString, msgType ), Q_ARG( QVariantMap, msg ) ); + QMetaObject::invokeMethod( receiver, slot.toLatin1(), Q_ARG( QString, msgType ), Q_ARG( QVariantMap, msg ) ); } else if ( msgType == "allPlaylists" ) { @@ -160,6 +159,63 @@ SpotifyAccount::resolverMessage( const QString &msgType, const QVariantMap &msg m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists ); } } + else if ( msgType == "tracksAdded" ) + { + const QString plid = msg.value( "playlistid" ).toString(); + // We should already be syncing this playlist if we get updates for it + Q_ASSERT( m_updaters.contains( plid ) ); + + if ( !m_updaters.contains( plid ) ) + return; + + SpotifyPlaylistUpdater* updater = m_updaters[ plid ]; + Q_ASSERT( updater->sync() ); + + const int startPos = msg.value( "startPosition" ).toInt(); + const QVariantList tracksList = msg.value( "tracks" ).toList(); + const QString newRev = msg.value( "revid" ).toString(); + const QString oldRev = msg.value( "oldRev" ).toString(); + + updater->spotifyTracksAdded( tracksList, startPos, newRev, oldRev ); + } + else if ( msgType == "tracksRemoved" ) + { + const QString plid = msg.value( "playlistid" ).toString(); + // We should already be syncing this playlist if we get updates for it + Q_ASSERT( m_updaters.contains( plid ) ); + + if ( !m_updaters.contains( plid ) ) + return; + + SpotifyPlaylistUpdater* updater = m_updaters[ plid ]; + Q_ASSERT( updater->sync() ); + + const QVariantList tracksList = msg.value( "tracks" ).toList(); + const QString newRev = msg.value( "revid" ).toString(); + const QString oldRev = msg.value( "oldRev" ).toString(); + + + updater->spotifyTracksRemoved( tracksList, newRev, oldRev ); + } + else if ( msgType == "tracksMoved" ) + { + const QString plid = msg.value( "playlistid" ).toString(); + // We should already be syncing this playlist if we get updates for it + Q_ASSERT( m_updaters.contains( plid ) ); + + if ( !m_updaters.contains( plid ) ) + return; + + SpotifyPlaylistUpdater* updater = m_updaters[ plid ]; + Q_ASSERT( updater->sync() ); + + const QVariantList tracksList = msg.value( "tracks" ).toList(); + const QString newRev = msg.value( "revid" ).toString(); + const QString oldRev = msg.value( "oldRev" ).toString(); + + + updater->spotifyTracksMoved( tracksList, newRev, oldRev ); + } else if ( msgType == "playlists" ) { // QList< Tomahawk::query_ptr > tracks; @@ -245,7 +301,7 @@ SpotifyAccount::saveConfig() msg[ "playlistid" ] = pl->plid; msg[ "sync" ] = pl->sync; - sendMessage( msg, "startPlaylistSyncWithPlaylist" ); + sendMessage( msg, this, "startPlaylistSyncWithPlaylist" ); } else stopPlaylistSync( pl ); @@ -260,30 +316,43 @@ SpotifyAccount::startPlaylistSyncWithPlaylist( const QString& msgType, const QVa qDebug() << Q_FUNC_INFO << "Got full spotify playlist body, creating a tomahawk playlist and enabling sync!!"; const QString id = msg.value( "id" ).toString(); const QString name = msg.value( "name" ).toString(); + const QString revid = msg.value( "revid" ).toString(); qDebug() << "Starting sync with pl:" << id << name; QVariantList tracks = msg.value( "tracks" ).toList(); // create a list of query/plentries directly - QList< query_ptr > queries; - foreach ( const QVariant& trackV, tracks ) + QList< query_ptr > queries = SpotifyPlaylistUpdater::variantToQueries( tracks ); + + /** + * Begin syncing a playlist. Two options: + * 1) This is a playlist that has never been synced to tomahawk. Create a new one + * and attach a new SpotifyPlaylistUpdater to it + * 2) This was previously synced, and has since been unsynced. THe playlist is still around + * with an inactive SpotifyPlaylistUpdater, so just enable it and bring it up to date by merging current with new + * TODO: show a warning( "Do you want to overwrite with spotify's version?" ) + */ + if ( m_updaters.contains( id ) ) { - const QVariantMap trackMap = trackV.toMap(); - qDebug() << "Adding spotify track to new tomahawk playlist:" << trackMap.value( "track" ).toString() << trackMap.value( "artist" ).toString() << trackMap.value( "album" ).toString(); - query_ptr q = Query::get( trackMap.value( "artist" ).toString(), trackMap.value( "track" ).toString(), trackMap.value( "album" ).toString(), uuid(), false ); - q->setProperty( "spotifytrackid", trackMap.value( "id" ) ); - - queries << q; + Q_ASSERT( m_updaters[ id ]->sync() == false ); /// Should have been unchecked/off before + m_updaters[ id ]->setSync( true ); +// m_updaters[ id ]-> + // TODO } + else + { + playlist_ptr plPtr = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), + uuid(), + name, + QString(), + QString(), + false, + queries ); - playlist_ptr plPtr = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), - uuid(), - name, - QString(), - QString(), - false, - queries ); - SpotifyPlaylistUpdater* updater = new SpotifyPlaylistUpdater( this, plPtr ); + SpotifyPlaylistUpdater* updater = new SpotifyPlaylistUpdater( this, revid, id, plPtr ); + updater->setSync( true ); + m_updaters[ id ] = updater; + } } @@ -341,12 +410,12 @@ SpotifyAccount::addPlaylist( const QString &qid, const QString& title, QList< To void -SpotifyAccount::sendMessage( const QVariantMap &m, const QString& slot ) +SpotifyAccount::sendMessage( const QVariantMap &m, QObject* obj, const QString& slot ) { QVariantMap msg = m; const QString qid = QUuid::createUuid().toString().replace( "{", "" ).replace( "}", "" ); - m_qidToSlotMap[ qid ] = slot; + m_qidToSlotMap[ qid ] = qMakePair( obj, slot ); msg[ "qid" ] = qid; m_spotifyResolver.data()->sendMessage( msg ); @@ -354,30 +423,16 @@ SpotifyAccount::sendMessage( const QVariantMap &m, const QString& slot ) void -SpotifyAccount::fetchFullPlaylist( SpotifyPlaylistInfo* playlist ) +SpotifyAccount::registerUpdaterForPlaylist( const QString& plId, SpotifyPlaylistUpdater* updater ) { - + m_updaters[ plId ] = updater; } void -SpotifyAccount::startPlaylistSync( SpotifyPlaylistInfo* playlist ) +SpotifyAccount::fetchFullPlaylist( SpotifyPlaylistInfo* playlist ) { - /** - * Begin syncing a playlist. Two options: - * 1) This is a playlist that has never been synced to tomahawk. Create a new one - * and attach a new SpotifyPlaylistUpdater to it - * 2) This was previously synced, and has since been unsynced. THe playlist is still around - * with an inactive SpotifyPlaylistUpdater, so just enable it and bring it up to date. - */ - if ( m_updaters.contains( playlist->plid ) ) - { - // update and re-sync - } - else - { - } } @@ -395,5 +450,5 @@ SpotifyAccount::loadPlaylists() // TODO cache this and only get changed? QVariantMap msg; msg[ "_msgtype" ] = "getAllPlaylists"; - sendMessage( msg, "allPlaylistsLoaded" ); + sendMessage( msg, this, "allPlaylistsLoaded" ); } diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index f8eac701a..d33512aa8 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -93,6 +93,9 @@ public: Tomahawk::playlist_ptr playlist; };*/ + void sendMessage( const QVariantMap& msg, QObject* receiver, const QString& slot ); + void registerUpdaterForPlaylist( const QString& plId, SpotifyPlaylistUpdater* updater ); + private slots: void resolverMessage( const QString& msgType, const QVariantMap& msg ); @@ -103,9 +106,7 @@ private slots: private: void init(); void loadPlaylists(); - void sendMessage( const QVariantMap& msg, const QString& slot ); - void startPlaylistSync( SpotifyPlaylistInfo* playlist ); void stopPlaylistSync( SpotifyPlaylistInfo* playlist ); void fetchFullPlaylist( SpotifyPlaylistInfo* playlist ); @@ -114,7 +115,7 @@ private: QWeakPointer m_configWidget; QWeakPointer m_spotifyResolver; - QMap m_qidToSlotMap; + QMap > m_qidToSlotMap; // List of synced spotify playlists in config UI QList< SpotifyPlaylistInfo* > m_allSpotifyPlaylists; diff --git a/src/accounts/spotify/SpotifyPlaylistUpdater.cpp b/src/accounts/spotify/SpotifyPlaylistUpdater.cpp index 785fdc6eb..96c75e140 100644 --- a/src/accounts/spotify/SpotifyPlaylistUpdater.cpp +++ b/src/accounts/spotify/SpotifyPlaylistUpdater.cpp @@ -40,15 +40,44 @@ SpotifyUpdaterFactory::create( const Tomahawk::playlist_ptr& pl ) } } - return new SpotifyPlaylistUpdater( m_account, pl ); + // Register the updater with the account + const QString spotifyId = QString( "playlistupdaters/%1" ).arg( pl->guid() ); + Q_ASSERT( !spotifyId.isEmpty() ); + SpotifyPlaylistUpdater* updater = new SpotifyPlaylistUpdater( m_account, pl ); + m_account->registerUpdaterForPlaylist( spotifyId, updater ); + + return updater; } SpotifyPlaylistUpdater::SpotifyPlaylistUpdater( SpotifyAccount* acct, const playlist_ptr& pl ) : PlaylistUpdaterInterface( pl ) , m_spotify( acct ) + , m_sync( false ) +{ + // values will be loaded from settings + init(); +} + + +SpotifyPlaylistUpdater::SpotifyPlaylistUpdater( SpotifyAccount* acct, const QString& revid, const QString& spotifyId, const playlist_ptr& pl ) + : PlaylistUpdaterInterface( pl ) + , m_spotify( acct ) + , m_latestRev( revid ) + , m_spotifyId( spotifyId ) + , m_sync( false ) +{ + init(); +} + + +void +SpotifyPlaylistUpdater::init() { + connect( playlist().data(), SIGNAL( tracksInserted( QList, int ) ), this, SLOT( tomahawkTracksInserted( QList, int ) ) ); + connect( playlist().data(), SIGNAL( tracksRemoved( QList ) ), this, SLOT( tomahawkTracksRemoved( QList ) ) ); + // TODO reorders in a playlist } @@ -60,30 +89,217 @@ SpotifyPlaylistUpdater::~SpotifyPlaylistUpdater() void SpotifyPlaylistUpdater::loadFromSettings( const QString& group ) { - + m_latestRev = TomahawkSettings::instance()->value( QString( "%1/latestrev" ).arg( group ) ).toString(); + m_sync = TomahawkSettings::instance()->value( QString( "%1/sync" ).arg( group ) ).toBool(); + m_spotifyId = TomahawkSettings::instance()->value( QString( "%1/spotifyId" ).arg( group ) ).toString(); } void SpotifyPlaylistUpdater::removeFromSettings( const QString& group ) const { - + TomahawkSettings::instance()->remove( QString( "%1/latestrev" ).arg( group ) ); + TomahawkSettings::instance()->remove( QString( "%1/sync" ).arg( group ) ); + TomahawkSettings::instance()->remove( QString( "%1/spotifyId" ).arg( group ) ); } void SpotifyPlaylistUpdater::saveToSettings( const QString& group ) const { - + TomahawkSettings::instance()->setValue( QString( "%1/latestrev" ).arg( group ), m_latestRev ); + TomahawkSettings::instance()->setValue( QString( "%1/sync" ).arg( group ), m_sync ); + TomahawkSettings::instance()->setValue( QString( "%1/spotifyId" ).arg( group ), m_spotifyId ); } QString SpotifyPlaylistUpdater::type() const { - return QString(); + return "spotify"; } + void -SpotifyPlaylistUpdater::updateNow() +SpotifyPlaylistUpdater::setSync( bool sync ) { + m_sync = sync; } + + +bool +SpotifyPlaylistUpdater::sync() const +{ + return m_sync; +} + + +void +SpotifyPlaylistUpdater::spotifyTracksAdded( const QVariantList& tracks, int startPos, const QString& newRev, const QString& oldRev ) +{ + const QList< query_ptr > queries = variantToQueries( tracks ); + + qDebug() << Q_FUNC_INFO << "inserting tracks in middle of tomahawk playlist, from spotify command!" << tracks << startPos << newRev << oldRev; + // Uh oh, dont' want to get out of sync!! + Q_ASSERT( m_latestRev == oldRev ); + m_latestRev = newRev; + + playlist()->insertEntries( queries, startPos, playlist()->currentrevision() ); +} + + +void +SpotifyPlaylistUpdater::spotifyTracksRemoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev ) +{ + qDebug() << Q_FUNC_INFO << "remove tracks in middle of tomahawk playlist, from spotify command!" << tracks << newRev << oldRev; + // Uh oh, dont' want to get out of sync!! + Q_ASSERT( m_latestRev == oldRev ); + m_latestRev = newRev; + + QList< plentry_ptr > entries = playlist()->entries(); + + // FIXME UGH have to do a manual lookup for each track we want to remove... any ideas? + foreach( const QVariant& blob, tracks ) + { + const QVariantMap trackMap = blob.toMap(); + for ( QList::iterator iter = entries.begin(); iter != entries.end(); ++iter ) + { + const QString trackId = iter->data()->query()->property( "spotifytrackid" ).toString(); + // easy case, we have a track id on both sides, so we're sure + if ( trackId.isEmpty() && trackId == trackMap.value( "id" ).toString() ) + { + iter = entries.erase( iter ); + continue; + } + + // fuzzy case, check metadata + if ( iter->data()->query()->track() == trackMap[ "track" ].toString() && + iter->data()->query()->artist() == trackMap[ "artist" ].toString() && + iter->data()->query()->album() == trackMap[ "album" ].toString() ) + { + iter = entries.erase( iter ); + continue; + } + } + } + + qDebug() << "We were asked to delete:" << tracks.size() << "tracks from the playlist, and we deleted:" << ( playlist()->entries().size() - entries.size() ); + playlist()->createNewRevision( uuid(), playlist()->currentrevision(), entries ); +} + + +void +SpotifyPlaylistUpdater::spotifyTracksMoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev ) +{ + // TODO +} + + +void +SpotifyPlaylistUpdater::tomahawkTracksInserted( const QList< plentry_ptr >& tracks, int pos ) +{ + // Notify the resolver that we've updated + qDebug() << Q_FUNC_INFO << "updating spotify resolver with inserted tracks at:" << pos << tracks; + QVariantMap msg; + msg[ "_msgtype" ] = "addTracksToPlaylist"; + msg[ "oldrev" ] = m_latestRev; + msg[ "startPosition" ] = pos; + + QVariantList tracksJson; + foreach ( const plentry_ptr& ple, tracks ) + { + const query_ptr q = ple->query(); + if ( q.isNull() ) + { + qDebug() << "Got null query_ptr in plentry_ptr!!!" << ple; + continue; + } + + tracksJson << queryToVariant( q ); + } + msg[ "tracks" ] = tracksJson; + + m_spotify->sendMessage( msg, this, "onTracksInsertedReturn" ); +} + + +void +SpotifyPlaylistUpdater::onTracksInsertedReturn( const QString& msgType, const QVariantMap& msg ) +{ + const bool success = msg.value( "success" ).toBool(); + + qDebug() << Q_FUNC_INFO << "GOT RETURN FOR tracksInserted call from spotify!" << msgType << msg << "Succeeded?" << success; + m_latestRev = msg.value( "revid" ).toString(); +} + + +void +SpotifyPlaylistUpdater::tomahawkTracksRemoved( const QList< query_ptr >& tracks ) +{ + qDebug() << Q_FUNC_INFO << "updating spotify resolver with removed tracks:" << tracks; + QVariantMap msg; + msg[ "_msgtype" ] = "removeTracksFromPlaylist"; + msg[ "oldrev" ] = m_latestRev; + msg[ "tracks" ] = queriesToVariant( tracks ); + + m_spotify->sendMessage( msg, this, "onTracksRemovedReturn" ); +} + + +void +SpotifyPlaylistUpdater::onTracksRemovedReturn( const QString& msgType, const QVariantMap& msg ) +{ + const bool success = msg.value( "success" ).toBool(); + + qDebug() << Q_FUNC_INFO << "GOT RETURN FOR tracksRemoved call from spotify!" << msgType << msg << "Succeeded?" << success; + m_latestRev = msg.value( "revid" ).toString(); +} + + +QVariantList +SpotifyPlaylistUpdater::queriesToVariant( const QList< query_ptr >& queries ) +{ + QVariantList tracksJson; + foreach ( const query_ptr& q, queries ) + { + QVariantMap m; + if ( q.isNull() ) + continue; + tracksJson << queryToVariant( q ); + } + + return tracksJson; +} + + +QVariant +SpotifyPlaylistUpdater::queryToVariant( const query_ptr& query ) +{ + QVariantMap m; + m[ "track" ] = query->track(); + m[ "artist" ] = query->artist(); + m[ "album" ] = query->album(); + + if ( !query->property( "spotifytrackid" ).isNull() ) + m[ "id" ] = query->property( "spotifytrackid" ); + + return m; +} + + +QList< query_ptr > +SpotifyPlaylistUpdater::variantToQueries( const QVariantList& list ) +{ + QList< query_ptr > queries; + foreach ( const QVariant& blob, list ) + { + QVariantMap trackMap = blob.toMap(); + const query_ptr q = Query::get( trackMap.value( "artist" ).toString(), trackMap.value( "track" ).toString(), trackMap.value( "album" ).toString(), uuid(), false ); + if ( trackMap.contains( "id" ) ) + q->setProperty( "spotifytrackid", trackMap.value( "id" ) ); + + queries << q; + } + + return queries; +} + diff --git a/src/accounts/spotify/SpotifyPlaylistUpdater.h b/src/accounts/spotify/SpotifyPlaylistUpdater.h index 24c39fb30..c6350f15c 100644 --- a/src/accounts/spotify/SpotifyPlaylistUpdater.h +++ b/src/accounts/spotify/SpotifyPlaylistUpdater.h @@ -20,6 +20,7 @@ #define SPOTIFYPLAYLISTUPDATER_H #include "playlist/PlaylistUpdaterInterface.h" +#include namespace Tomahawk { namespace Accounts { @@ -27,25 +28,53 @@ namespace Accounts { } } - class SpotifyPlaylistUpdater : public Tomahawk::PlaylistUpdaterInterface { Q_OBJECT + + friend class Tomahawk::Accounts::SpotifyAccount; public: + // used when creating anew + SpotifyPlaylistUpdater( Tomahawk::Accounts::SpotifyAccount* acct, const QString& revid, const QString& spotifyId, const Tomahawk::playlist_ptr& pl ); + + // used when inflating from config file SpotifyPlaylistUpdater( Tomahawk::Accounts::SpotifyAccount* acct, const Tomahawk::playlist_ptr& pl ); virtual ~SpotifyPlaylistUpdater(); virtual QString type() const; - virtual void updateNow(); + virtual void updateNow() {} + + bool sync() const; + void setSync( bool sync ); + + /// Spotify callbacks when we are directly instructed from the resolver + void spotifyTracksAdded( const QVariantList& tracks, int startPos, const QString& newRev, const QString& oldRev ); + void spotifyTracksRemoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev ); + void spotifyTracksMoved( const QVariantList& tracks, const QString& newRev, const QString& oldRev ); protected: virtual void removeFromSettings(const QString& group) const; virtual void saveToSettings(const QString& group) const; virtual void loadFromSettings(const QString& group); +private slots: + void tomahawkTracksInserted( const QList& ,int ); + void tomahawkTracksRemoved( const QList& ); + + // SpotifyResolver message handlers, all take msgtype, msg as argument + void onTracksInsertedReturn( const QString& msgType, const QVariantMap& msg ); + void onTracksRemovedReturn( const QString& msgType, const QVariantMap& msg ); + private: + void init(); + static QVariantList queriesToVariant( const QList< Tomahawk::query_ptr >& queries ); + static QVariant queryToVariant( const Tomahawk::query_ptr& query ); + static QList< Tomahawk::query_ptr > variantToQueries( const QVariantList& list ); + Tomahawk::Accounts::SpotifyAccount* m_spotify; + QString m_latestRev, m_spotifyId; + bool m_sync; }; diff --git a/src/libtomahawk/playlist.cpp b/src/libtomahawk/playlist.cpp index 04cc0d6f6..fb238f31a 100644 --- a/src/libtomahawk/playlist.cpp +++ b/src/libtomahawk/playlist.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -517,6 +518,27 @@ Playlist::addEntries( const QList& queries, const QString& oldrev ) createNewRevision( newrev, oldrev, el ); } +void +Playlist::insertEntries( const QList< query_ptr >& queries, const int position, const QString& oldrev ) +{ + QList toInsert = entriesFromQueries( queries, true ); + QList entries = m_entries; + + Q_ASSERT( position < m_entries.size() ); + if ( position >= m_entries.size() ) + { + qWarning() << "ERROR trying to insert tracks past end of playlist! Appending!!"; + addEntries( queries, oldrev ); + return; + } + + for ( QList< plentry_ptr >::iterator iter = toInsert.end(); iter != toInsert.begin(); iter-- ) + entries.insert( position, *iter ); + + qDebug() << "Done inserting playlist entries in the middle of the playlist! Committing..."; + createNewRevision( uuid(), oldrev, entries ); +} + QList Playlist::entriesFromQueries( const QList& queries, bool clearFirst ) diff --git a/src/libtomahawk/playlist.h b/src/libtomahawk/playlist.h index 5cc6c70cf..6d44483dd 100644 --- a/src/libtomahawk/playlist.h +++ b/src/libtomahawk/playlist.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +38,8 @@ class DatabaseCommand_LoadAllPlaylists; class DatabaseCommand_LoadAllSortedPlaylists; class DatabaseCommand_SetPlaylistRevision; class DatabaseCommand_CreatePlaylist; +class PlaylistModel; + namespace Tomahawk { @@ -116,20 +119,21 @@ public: class DLLEXPORT Playlist : public QObject { -Q_OBJECT -Q_PROPERTY( QString guid READ guid WRITE setGuid ) -Q_PROPERTY( QString currentrevision READ currentrevision WRITE setCurrentrevision ) -Q_PROPERTY( QString title READ title WRITE setTitle ) -Q_PROPERTY( QString info READ info WRITE setInfo ) -Q_PROPERTY( QString creator READ creator WRITE setCreator ) -Q_PROPERTY( unsigned int createdon READ createdOn WRITE setCreatedOn ) -Q_PROPERTY( bool shared READ shared WRITE setShared ) + Q_OBJECT + Q_PROPERTY( QString guid READ guid WRITE setGuid ) + Q_PROPERTY( QString currentrevision READ currentrevision WRITE setCurrentrevision ) + Q_PROPERTY( QString title READ title WRITE setTitle ) + Q_PROPERTY( QString info READ info WRITE setInfo ) + Q_PROPERTY( QString creator READ creator WRITE setCreator ) + Q_PROPERTY( unsigned int createdon READ createdOn WRITE setCreatedOn ) + Q_PROPERTY( bool shared READ shared WRITE setShared ) friend class ::DatabaseCommand_LoadAllPlaylists; friend class ::DatabaseCommand_LoadAllSortedPlaylists; friend class ::DatabaseCommand_SetPlaylistRevision; friend class ::DatabaseCommand_CreatePlaylist; friend class DynamicPlaylist; +friend class ::PlaylistModel; public: virtual ~Playlist(); @@ -165,6 +169,7 @@ public: const QList< plentry_ptr >& entries() { return m_entries; } virtual void addEntry( const Tomahawk::query_ptr& query, const QString& oldrev ); virtual void addEntries( const QList& queries, const QString& oldrev ); + virtual void insertEntries( const QList& queries, const int position, const QString& oldrev ); // // these need to exist and be public for the json serialization stuff @@ -182,6 +187,7 @@ public: QList entriesFromQueries( const QList& queries, bool clearFirst = false ); + void setUpdater( PlaylistUpdaterInterface* interface ) { m_updater = interface; } PlaylistUpdaterInterface* updater() const { return m_updater; } @@ -206,6 +212,13 @@ signals: /// was deleted, eh? void deleted( const Tomahawk::playlist_ptr& pl ); + /// Notification for tracks being inserted at a specific point + /// Contiguous range from startPosition + void tracksInserted( const QList< Tomahawk::plentry_ptr >& tracks, int startPosition ); + + /// Notification for tracks being removed from playlist + void tracksRemoved( const QList< Tomahawk::query_ptr >& tracks ); + public slots: // want to update the playlist from the model? // generate a newrev using uuid() and call this: diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 080af747e..5a0282eed 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -40,6 +40,7 @@ PlaylistModel::PlaylistModel( QObject* parent ) : TrackModel( parent ) , m_isTemporary( false ) , m_changesOngoing( false ) + , m_savedInsertPos( -1 ) { m_dropStorage.parent = QPersistentModelIndex(); m_dropStorage.row = -10; @@ -202,6 +203,9 @@ PlaylistModel::insert( const QList< Tomahawk::plentry_ptr >& entries, int row ) crows.first = c; crows.second = c + entries.count() - 1; + m_savedInsertPos = row; + m_savedInsertTracks = entries; + emit beginInsertRows( QModelIndex(), crows.first, crows.second ); QList< Tomahawk::query_ptr > queries; @@ -398,6 +402,18 @@ PlaylistModel::endPlaylistChanges() { m_playlist->createNewRevision( newrev, m_playlist->currentrevision(), l ); } + + if ( m_savedInsertPos >= 0 ) + { + emit m_playlist->tracksInserted( m_savedInsertTracks, m_savedInsertPos ); + m_savedInsertPos = -1; + m_savedInsertTracks.clear(); + } + else if ( !m_savedRemoveTracks.isEmpty() ) + { + emit m_playlist->tracksRemoved( m_savedRemoveTracks ); + m_savedRemoveTracks.clear(); + } } @@ -442,6 +458,9 @@ PlaylistModel::remove( const QModelIndex& index, bool moreToCome ) if ( !m_changesOngoing ) beginPlaylistChanges(); + if ( item ) + m_savedRemoveTracks << item->query(); + TrackModel::remove( index, moreToCome ); if ( !moreToCome ) diff --git a/src/libtomahawk/playlist/playlistmodel.h b/src/libtomahawk/playlist/playlistmodel.h index 01c3ff3b3..aca54fcc3 100644 --- a/src/libtomahawk/playlist/playlistmodel.h +++ b/src/libtomahawk/playlist/playlistmodel.h @@ -100,6 +100,10 @@ private: QList< Tomahawk::Query* > m_waitingForResolved; QStringList m_waitForRevision; + int m_savedInsertPos; + QList< Tomahawk::plentry_ptr > m_savedInsertTracks; + QList< Tomahawk::query_ptr > m_savedRemoveTracks; + DropStorageData m_dropStorage; };