1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 03:10:12 +02:00

Add an intermediate LovedTracksModel

This commit is contained in:
Uwe L. Korn
2013-07-31 20:35:56 +02:00
parent c7e5fc62c2
commit 6fd7c83fad
13 changed files with 637 additions and 315 deletions

View File

@@ -71,6 +71,7 @@ set( libGuiSources
playlist/ColumnView.cpp playlist/ColumnView.cpp
playlist/TreeWidget.cpp playlist/TreeWidget.cpp
playlist/ViewHeader.cpp playlist/ViewHeader.cpp
playlist/LovedTracksModel.cpp
playlist/TopLovedTracksModel.cpp playlist/TopLovedTracksModel.cpp
playlist/RecentlyAddedModel.cpp playlist/RecentlyAddedModel.cpp
playlist/RecentlyPlayedModel.cpp playlist/RecentlyPlayedModel.cpp

View File

@@ -0,0 +1,122 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LovedTracksModel_p.h"
#include "SourceList.h"
LovedTracksModel::LovedTracksModel( QObject *parent )
: PlaylistModel( parent, new LovedTracksModelPrivate( this ) )
{
Q_D( LovedTracksModel );
d->smoothingTimer.setInterval( 300 );
d->smoothingTimer.setSingleShot( true );
connect( &d->smoothingTimer, SIGNAL( timeout() ), this, SLOT( loadTracks() ) );
}
LovedTracksModel::~LovedTracksModel()
{
}
bool
LovedTracksModel::isTemporary() const
{
return true;
}
void
LovedTracksModel::loadTracks()
{
// Implement this in subclasses.
}
void
LovedTracksModel::onSourcesReady()
{
Q_D( LovedTracksModel );
Q_ASSERT( d->source.isNull() );
loadTracks();
foreach ( const Tomahawk::source_ptr& source, SourceList::instance()->sources() )
onSourceAdded( source );
}
void
LovedTracksModel::onSourceAdded( const Tomahawk::source_ptr& source )
{
connect( source.data(), SIGNAL( socialAttributesChanged( QString ) ), SLOT( onTrackLoved() ), Qt::UniqueConnection );
}
void
LovedTracksModel::onTrackLoved()
{
Q_D( LovedTracksModel );
d->smoothingTimer.start();
}
void
LovedTracksModel::tracksLoaded( QList< Tomahawk::query_ptr > newLoved )
{
finishLoading();
QList< Tomahawk::query_ptr > tracks;
foreach ( const Tomahawk::plentry_ptr ple, playlistEntries() )
tracks << ple->query();
bool changed = false;
QList< Tomahawk::query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, newLoved, changed );
if ( changed )
{
QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
clear();
appendEntries( el );
}
}
void
LovedTracksModel::setSource( const Tomahawk::source_ptr& source )
{
Q_D( LovedTracksModel );
d->source = source;
if ( source.isNull() )
{
if ( SourceList::instance()->isReady() )
onSourcesReady();
else
connect( SourceList::instance(), SIGNAL( ready() ), SLOT( onSourcesReady() ) );
connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) );
}
else
{
onSourceAdded( source );
loadTracks();
}
}

View File

@@ -0,0 +1,57 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LOVEDTRACKSMODEL_H
#define LOVEDTRACKSMODEL_H
#include "PlaylistModel.h"
class LovedTracksModelPrivate;
class DLLEXPORT LovedTracksModel : public PlaylistModel
{
Q_OBJECT
public:
explicit LovedTracksModel( QObject* parent = 0 );
virtual ~LovedTracksModel();
unsigned int limit() const;
void setLimit( unsigned int limit );
bool isTemporary() const;
public slots:
void setSource( const Tomahawk::source_ptr& source );
private slots:
virtual void loadTracks();
void onSourcesReady();
void onSourceAdded( const Tomahawk::source_ptr& source );
void onTrackLoved();
void tracksLoaded( QList<Tomahawk::query_ptr> );
private:
Q_DECLARE_PRIVATE( LovedTracksModel )
};
#endif // LOVEDTRACKSMODEL_H

View File

@@ -0,0 +1,47 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LOVEDTRACKSMODEL_P_H
#define LOVEDTRACKSMODEL_P_H
#include "LovedTracksModel.h"
#include "PlaylistModel_p.h"
#include <QTimer>
class LovedTracksModelPrivate : public PlaylistModelPrivate
{
public:
LovedTracksModelPrivate( LovedTracksModel* q )
: PlaylistModelPrivate( q )
, limit( defaultNumberOfLovedTracks )
{
}
Q_DECLARE_PUBLIC( LovedTracksModel )
static const uint defaultNumberOfLovedTracks = 25;
protected:
uint limit;
Tomahawk::source_ptr source;
QTimer smoothingTimer;
};
#endif // LOVEDTRACKSMODEL_P_H

View File

@@ -18,7 +18,7 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "PlayableModel.h" #include "PlayableModel_p.h"
#include "audio/AudioEngine.h" #include "audio/AudioEngine.h"
#include "utils/TomahawkUtils.h" #include "utils/TomahawkUtils.h"
@@ -40,24 +40,40 @@
using namespace Tomahawk; using namespace Tomahawk;
PlayableModel::PlayableModel( QObject* parent, bool loading ) void
: QAbstractItemModel( parent ) PlayableModel::init()
, m_rootItem( new PlayableItem( 0 ) )
, m_readOnly( true )
, m_loading( loading )
{ {
Q_D( PlayableModel );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection );
connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection );
m_header << tr( "Artist" ) << tr( "Title" ) << tr( "Composer" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" ) d->header << tr( "Artist" ) << tr( "Title" ) << tr( "Composer" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" )
<< tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Accuracy" ) << tr( "Name" ); << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Accuracy" ) << tr( "Name" );
} }
PlayableModel::PlayableModel( QObject* parent, bool loading )
: QAbstractItemModel( parent )
, d_ptr( new PlayableModelPrivate( this, loading ) )
{
init();
}
PlayableModel::PlayableModel( QObject* parent, PlayableModelPrivate* d )
: QAbstractItemModel( parent )
, d_ptr( d )
{
init();
}
PlayableModel::~PlayableModel() PlayableModel::~PlayableModel()
{ {
Q_D( PlayableModel );
tDebug() << Q_FUNC_INFO; tDebug() << Q_FUNC_INFO;
delete m_rootItem; delete d->rootItem;
} }
@@ -71,7 +87,8 @@ PlayableModel::createIndex( int row, int column, PlayableItem* item ) const
QModelIndex QModelIndex
PlayableModel::index( int row, int column, const QModelIndex& parent ) const PlayableModel::index( int row, int column, const QModelIndex& parent ) const
{ {
if ( !m_rootItem || row < 0 || column < 0 ) Q_D( const PlayableModel );
if ( !d->rootItem || row < 0 || column < 0 )
return QModelIndex(); return QModelIndex();
PlayableItem* parentItem = itemFromIndex( parent ); PlayableItem* parentItem = itemFromIndex( parent );
@@ -109,11 +126,12 @@ PlayableModel::columnCount( const QModelIndex& parent ) const
bool bool
PlayableModel::hasChildren( const QModelIndex& parent ) const PlayableModel::hasChildren( const QModelIndex& parent ) const
{ {
Q_D( const PlayableModel );
PlayableItem* parentItem = itemFromIndex( parent ); PlayableItem* parentItem = itemFromIndex( parent );
if ( !parentItem ) if ( !parentItem )
return false; return false;
if ( parentItem == m_rootItem ) if ( parentItem == d->rootItem )
return true; return true;
return ( !parentItem->artist().isNull() || !parentItem->album().isNull() ); return ( !parentItem->artist().isNull() || !parentItem->album().isNull() );
@@ -143,28 +161,32 @@ PlayableModel::parent( const QModelIndex& child ) const
bool bool
PlayableModel::isReadOnly() const PlayableModel::isReadOnly() const
{ {
return m_readOnly; Q_D( const PlayableModel );
return d->readOnly;
} }
void void
PlayableModel::setReadOnly( bool b ) PlayableModel::setReadOnly( bool b )
{ {
m_readOnly = b; Q_D( PlayableModel );
d->readOnly = b;
} }
bool bool
PlayableModel::isLoading() const PlayableModel::isLoading() const
{ {
return m_loading; Q_D( const PlayableModel );
return d->loading;
} }
QString QString
PlayableModel::title() const PlayableModel::title() const
{ {
return m_title; Q_D( const PlayableModel );
return d->title;
} }
@@ -364,12 +386,13 @@ PlayableModel::data( const QModelIndex& index, int role ) const
QVariant QVariant
PlayableModel::headerData( int section, Qt::Orientation orientation, int role ) const PlayableModel::headerData( int section, Qt::Orientation orientation, int role ) const
{ {
Q_D( const PlayableModel );
Q_UNUSED( orientation ); Q_UNUSED( orientation );
if ( role == Qt::DisplayRole && section >= 0 ) if ( role == Qt::DisplayRole && section >= 0 )
{ {
if ( section < m_header.count() ) if ( section < d->header.count() )
return m_header.at( section ); return d->header.at( section );
else else
return tr( "Name" ); return tr( "Name" );
} }
@@ -386,7 +409,8 @@ PlayableModel::headerData( int section, Qt::Orientation orientation, int role )
void void
PlayableModel::setCurrentIndex( const QModelIndex& index ) PlayableModel::setCurrentIndex( const QModelIndex& index )
{ {
PlayableItem* oldEntry = itemFromIndex( m_currentIndex ); Q_D( PlayableModel );
PlayableItem* oldEntry = itemFromIndex( d->currentIndex );
if ( oldEntry ) if ( oldEntry )
{ {
oldEntry->setIsPlaying( false ); oldEntry->setIsPlaying( false );
@@ -395,14 +419,14 @@ PlayableModel::setCurrentIndex( const QModelIndex& index )
PlayableItem* entry = itemFromIndex( index ); PlayableItem* entry = itemFromIndex( index );
if ( index.isValid() && entry && !entry->query().isNull() ) if ( index.isValid() && entry && !entry->query().isNull() )
{ {
m_currentIndex = index; d->currentIndex = index;
m_currentUuid = entry->query()->id(); d->currentUuid = entry->query()->id();
entry->setIsPlaying( true ); entry->setIsPlaying( true );
} }
else else
{ {
m_currentIndex = QModelIndex(); d->currentIndex = QModelIndex();
m_currentUuid = QString(); d->currentUuid = QString();
} }
emit currentIndexChanged(); emit currentIndexChanged();
@@ -431,14 +455,16 @@ PlayableModel::flags( const QModelIndex& index ) const
QPersistentModelIndex QPersistentModelIndex
PlayableModel::currentItem() PlayableModel::currentItem()
{ {
return m_currentIndex; Q_D( PlayableModel );
return d->currentIndex;
} }
QID QID
PlayableModel::currentItemUuid() PlayableModel::currentItemUuid()
{ {
return m_currentUuid; Q_D( PlayableModel );
return d->currentUuid;
} }
@@ -597,14 +623,15 @@ PlayableModel::mimeData( const QModelIndexList &indexes ) const
void void
PlayableModel::clear() PlayableModel::clear()
{ {
Q_D( PlayableModel );
if ( rowCount( QModelIndex() ) ) if ( rowCount( QModelIndex() ) )
{ {
finishLoading(); finishLoading();
emit beginResetModel(); emit beginResetModel();
delete m_rootItem; delete d->rootItem;
m_rootItem = 0; d->rootItem = 0;
m_rootItem = new PlayableItem( 0 ); d->rootItem = new PlayableItem( 0 );
emit endResetModel(); emit endResetModel();
} }
} }
@@ -613,10 +640,11 @@ PlayableModel::clear()
QList< query_ptr > QList< query_ptr >
PlayableModel::queries() const PlayableModel::queries() const
{ {
Q_ASSERT( m_rootItem ); Q_D( const PlayableModel );
Q_ASSERT( d->rootItem );
QList< query_ptr > tracks; QList< query_ptr > tracks;
foreach ( PlayableItem* item, m_rootItem->children ) foreach ( PlayableItem* item, d->rootItem->children )
{ {
tracks << item->query(); tracks << item->query();
} }
@@ -629,6 +657,7 @@ template <typename T>
void void
PlayableModel::insertInternal( const QList< T >& items, int row, const QList< Tomahawk::PlaybackLog >& logs ) PlayableModel::insertInternal( const QList< T >& items, int row, const QList< Tomahawk::PlaybackLog >& logs )
{ {
Q_D( PlayableModel );
if ( !items.count() ) if ( !items.count() )
{ {
emit itemCountChanged( rowCount( QModelIndex() ) ); emit itemCountChanged( rowCount( QModelIndex() ) );
@@ -648,7 +677,7 @@ PlayableModel::insertInternal( const QList< T >& items, int row, const QList< To
PlayableItem* plitem; PlayableItem* plitem;
foreach ( const T& item, items ) foreach ( const T& item, items )
{ {
plitem = new PlayableItem( item, m_rootItem, row + i ); plitem = new PlayableItem( item, d->rootItem, row + i );
plitem->index = createIndex( row + i, 0, plitem ); plitem->index = createIndex( row + i, 0, plitem );
if ( plitem->query() ) if ( plitem->query() )
{ {
@@ -683,6 +712,7 @@ PlayableModel::remove( int row, bool moreToCome )
void void
PlayableModel::removeIndex( const QModelIndex& index, bool moreToCome ) PlayableModel::removeIndex( const QModelIndex& index, bool moreToCome )
{ {
Q_D( PlayableModel );
if ( QThread::currentThread() != thread() ) if ( QThread::currentThread() != thread() )
{ {
QMetaObject::invokeMethod( this, "remove", QMetaObject::invokeMethod( this, "remove",
@@ -698,7 +728,7 @@ PlayableModel::removeIndex( const QModelIndex& index, bool moreToCome )
PlayableItem* item = itemFromIndex( index ); PlayableItem* item = itemFromIndex( index );
if ( item ) if ( item )
{ {
if ( index == m_currentIndex ) if ( index == d->currentIndex )
setCurrentIndex( QModelIndex() ); setCurrentIndex( QModelIndex() );
emit beginRemoveRows( index.parent(), index.row(), index.row() ); emit beginRemoveRows( index.parent(), index.row(), index.row() );
@@ -741,18 +771,19 @@ PlayableModel::removeIndexes( const QList<QPersistentModelIndex>& indexes )
} }
} }
PlayableItem* PlayableItem*
PlayableModel::rootItem() const PlayableModel::rootItem() const
{ {
return m_rootItem; Q_D( const PlayableModel );
return d->rootItem;
} }
void void
PlayableModel::onPlaybackStarted( const Tomahawk::result_ptr& result ) PlayableModel::onPlaybackStarted( const Tomahawk::result_ptr& result )
{ {
PlayableItem* oldEntry = itemFromIndex( m_currentIndex ); Q_D( PlayableModel );
PlayableItem* oldEntry = itemFromIndex( d->currentIndex );
if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) ) if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) )
{ {
oldEntry->setIsPlaying( false ); oldEntry->setIsPlaying( false );
@@ -763,7 +794,8 @@ PlayableModel::onPlaybackStarted( const Tomahawk::result_ptr& result )
void void
PlayableModel::onPlaybackStopped() PlayableModel::onPlaybackStopped()
{ {
PlayableItem* oldEntry = itemFromIndex( m_currentIndex ); Q_D( PlayableModel );
PlayableItem* oldEntry = itemFromIndex( d->currentIndex );
if ( oldEntry ) if ( oldEntry )
{ {
oldEntry->setIsPlaying( false ); oldEntry->setIsPlaying( false );
@@ -855,7 +887,8 @@ PlayableModel::onDataChanged()
void void
PlayableModel::startLoading() PlayableModel::startLoading()
{ {
m_loading = true; Q_D( PlayableModel );
d->loading = true;
emit loadingStarted(); emit loadingStarted();
} }
@@ -863,7 +896,8 @@ PlayableModel::startLoading()
void void
PlayableModel::finishLoading() PlayableModel::finishLoading()
{ {
m_loading = false; Q_D( PlayableModel );
d->loading = false;
emit loadingFinished(); emit loadingFinished();
} }
@@ -871,13 +905,14 @@ PlayableModel::finishLoading()
PlayableItem* PlayableItem*
PlayableModel::itemFromIndex( const QModelIndex& index ) const PlayableModel::itemFromIndex( const QModelIndex& index ) const
{ {
Q_D( const PlayableModel );
if ( index.isValid() ) if ( index.isValid() )
{ {
return static_cast<PlayableItem*>( index.internalPointer() ); return static_cast<PlayableItem*>( index.internalPointer() );
} }
else else
{ {
return m_rootItem; return d->rootItem;
} }
} }
@@ -1002,7 +1037,8 @@ PlayableModel::insertQueries( const QList< Tomahawk::query_ptr >& queries, int r
void void
PlayableModel::setTitle( const QString& title ) PlayableModel::setTitle( const QString& title )
{ {
m_title = title; Q_D( PlayableModel );
d->title = title;
emit changed(); emit changed();
} }
@@ -1010,14 +1046,16 @@ PlayableModel::setTitle( const QString& title )
QString QString
PlayableModel::description() const PlayableModel::description() const
{ {
return m_description; Q_D( const PlayableModel );
return d->description;
} }
void void
PlayableModel::setDescription( const QString& description ) PlayableModel::setDescription( const QString& description )
{ {
m_description = description; Q_D( PlayableModel );
d->description = description;
emit changed(); emit changed();
} }
@@ -1025,14 +1063,16 @@ PlayableModel::setDescription( const QString& description )
QPixmap QPixmap
PlayableModel::icon() const PlayableModel::icon() const
{ {
return m_icon; Q_D( const PlayableModel );
return d->icon;
} }
void void
PlayableModel::setIcon( const QPixmap& pixmap ) PlayableModel::setIcon( const QPixmap& pixmap )
{ {
m_icon = pixmap; Q_D( PlayableModel );
d->icon = pixmap;
emit changed(); emit changed();
} }

View File

@@ -3,6 +3,7 @@
* Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011 Leo Franchi <lfranchi@kde.org> * Copyright 2011 Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org> * Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -22,17 +23,16 @@
#ifndef PLAYABLEMODEL_H #ifndef PLAYABLEMODEL_H
#define PLAYABLEMODEL_H #define PLAYABLEMODEL_H
#include <QAbstractItemModel> #include "DllMacro.h"
#include <QPixmap> #include "PlaybackLog.h"
#include "PlaylistInterface.h"
#include "Typedefs.h" #include "Typedefs.h"
#include "DllMacro.h" #include <QAbstractItemModel>
class QMetaData; class QMetaData;
class PlayableItem; class PlayableItem;
class PlayableModelPrivate;
class DLLEXPORT PlayableModel : public QAbstractItemModel class DLLEXPORT PlayableModel : public QAbstractItemModel
{ {
@@ -175,6 +175,9 @@ public slots:
virtual void setShuffled( bool /*shuffled*/ ) {} virtual void setShuffled( bool /*shuffled*/ ) {}
protected: protected:
QScopedPointer<PlayableModelPrivate> d_ptr;
PlayableModel( QObject* parent, PlayableModelPrivate* d );
PlayableItem* rootItem() const; PlayableItem* rootItem() const;
QModelIndex createIndex( int row, int column, PlayableItem* item = 0 ) const; QModelIndex createIndex( int row, int column, PlayableItem* item = 0 ) const;
@@ -188,25 +191,14 @@ private slots:
void onPlaybackStopped(); void onPlaybackStopped();
private: private:
void init();
template <typename T> template <typename T>
void insertInternal( const QList< T >& items, int row, const QList< Tomahawk::PlaybackLog >& logs = QList< Tomahawk::PlaybackLog >() ); void insertInternal( const QList< T >& items, int row, const QList< Tomahawk::PlaybackLog >& logs = QList< Tomahawk::PlaybackLog >() );
QString scoreText( float score ) const; QString scoreText( float score ) const;
Qt::Alignment columnAlignment( int column ) const; Qt::Alignment columnAlignment( int column ) const;
PlayableItem* m_rootItem; Q_DECLARE_PRIVATE( PlayableModel )
QPersistentModelIndex m_currentIndex;
Tomahawk::QID m_currentUuid;
bool m_readOnly;
QString m_title;
QString m_description;
QPixmap m_icon;
QStringList m_header;
bool m_loading;
}; };
#endif // PLAYABLEMODEL_H #endif // PLAYABLEMODEL_H

View File

@@ -0,0 +1,63 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2011 Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef PLAYABLEMODEL_P_H
#define PLAYABLEMODEL_P_H
#include "PlayableModel.h"
#include "PlayableItem.h"
#include <QPixmap>
#include <QStringList>
class PlayableModelPrivate
{
public:
PlayableModelPrivate( PlayableModel* q, bool _loading )
: q_ptr( q )
, rootItem( new PlayableItem( 0 ) )
, readOnly( true )
, loading( _loading )
{
}
PlayableModel* q_ptr;
Q_DECLARE_PUBLIC( PlayableModel )
private:
PlayableItem* rootItem;
QPersistentModelIndex currentIndex;
Tomahawk::QID currentUuid;
bool readOnly;
QString title;
QString description;
QPixmap icon;
QStringList header;
bool loading;
};
#endif // PLAYABLEMODEL_P_H

View File

@@ -18,7 +18,7 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "PlaylistModel.h" #include "PlaylistModel_p.h"
#include <QMimeData> #include <QMimeData>
#include <QTreeView> #include <QTreeView>
@@ -41,21 +41,31 @@
using namespace Tomahawk; using namespace Tomahawk;
PlaylistModel::PlaylistModel( QObject* parent ) void
: PlayableModel( parent ) PlaylistModel::init()
, m_isTemporary( false )
, m_changesOngoing( false )
, m_isLoading( false )
, m_acceptPlayableQueriesOnly( false )
, m_savedInsertPos( -1 )
{ {
m_dropStorage.parent = QPersistentModelIndex(); Q_D( PlaylistModel );
m_dropStorage.row = -10; d->dropStorage.parent = QPersistentModelIndex();
d->dropStorage.row = -10;
setReadOnly( true ); setReadOnly( true );
} }
PlaylistModel::PlaylistModel( QObject* parent )
: PlayableModel( parent, new PlaylistModelPrivate( this ) )
{
init();
}
PlaylistModel::PlaylistModel( QObject* parent, PlaylistModelPrivate* d )
: PlayableModel( parent, d )
{
init();
}
PlaylistModel::~PlaylistModel() PlaylistModel::~PlaylistModel()
{ {
} }
@@ -64,9 +74,10 @@ PlaylistModel::~PlaylistModel()
QString QString
PlaylistModel::guid() const PlaylistModel::guid() const
{ {
if ( !m_playlist.isNull() ) Q_D( const PlaylistModel );
if ( !d->playlist.isNull() )
{ {
return QString( "playlistmodel/%1" ).arg( m_playlist->guid() ); return QString( "playlistmodel/%1" ).arg( d->playlist->guid() );
} }
else else
return QString(); return QString();
@@ -76,31 +87,32 @@ PlaylistModel::guid() const
void void
PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries ) PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries )
{ {
if ( !m_playlist.isNull() ) Q_D( PlaylistModel );
if ( !d->playlist.isNull() )
{ {
disconnect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) ); disconnect( d->playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) );
disconnect( m_playlist.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ), this, SIGNAL( playlistDeleted() ) ); disconnect( d->playlist.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ), this, SIGNAL( playlistDeleted() ) );
disconnect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onPlaylistChanged() ) ); disconnect( d->playlist.data(), SIGNAL( changed() ), this, SLOT( onPlaylistChanged() ) );
} }
m_isLoading = true; d->isLoading = true;
if ( loadEntries ) if ( loadEntries )
clear(); clear();
m_playlist = playlist; d->playlist = playlist;
connect( playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) ); connect( playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) );
connect( playlist.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ), SIGNAL( playlistDeleted() ) ); connect( playlist.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ), SIGNAL( playlistDeleted() ) );
connect( playlist.data(), SIGNAL( changed() ), SLOT( onPlaylistChanged() ) ); connect( playlist.data(), SIGNAL( changed() ), SLOT( onPlaylistChanged() ) );
setReadOnly( !m_playlist->author()->isLocal() ); setReadOnly( !d->playlist->author()->isLocal() );
m_isTemporary = false; d->isTemporary = false;
onPlaylistChanged(); onPlaylistChanged();
if ( !loadEntries ) if ( !loadEntries )
{ {
m_isLoading = false; d->isLoading = false;
return; return;
} }
@@ -110,18 +122,19 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn
appendEntries( entries ); appendEntries( entries );
m_isLoading = false; d->isLoading = false;
} }
void void
PlaylistModel::onPlaylistChanged() PlaylistModel::onPlaylistChanged()
{ {
QString age = TomahawkUtils::ageToString( QDateTime::fromTime_t( m_playlist->createdOn() ), true ); Q_D( PlaylistModel );
QString age = TomahawkUtils::ageToString( QDateTime::fromTime_t( d->playlist->createdOn() ), true );
QString desc; QString desc;
if ( m_playlist->creator().isEmpty() ) if ( d->playlist->creator().isEmpty() )
{ {
if ( m_playlist->author()->isLocal() ) if ( d->playlist->author()->isLocal() )
{ {
desc = tr( "A playlist you created %1." ) desc = tr( "A playlist you created %1." )
.arg( age ); .arg( age );
@@ -129,18 +142,18 @@ PlaylistModel::onPlaylistChanged()
else else
{ {
desc = tr( "A playlist by %1, created %2." ) desc = tr( "A playlist by %1, created %2." )
.arg( m_playlist->author()->friendlyName() ) .arg( d->playlist->author()->friendlyName() )
.arg( age ); .arg( age );
} }
} }
else else
{ {
desc = tr( "A playlist by %1, created %2." ) desc = tr( "A playlist by %1, created %2." )
.arg( m_playlist->creator() ) .arg( d->playlist->creator() )
.arg( age ); .arg( age );
} }
setTitle( m_playlist->title() ); setTitle( d->playlist->title() );
setDescription( desc ); setDescription( desc );
emit playlistChanged(); emit playlistChanged();
@@ -150,7 +163,8 @@ PlaylistModel::onPlaylistChanged()
void void
PlaylistModel::clear() PlaylistModel::clear()
{ {
m_waitingForResolved.clear(); Q_D( PlaylistModel );
d->waitingForResolved.clear();
PlayableModel::clear(); PlayableModel::clear();
} }
@@ -165,6 +179,7 @@ PlaylistModel::appendEntries( const QList< plentry_ptr >& entries )
void void
PlaylistModel::insertAlbums( const QList< Tomahawk::album_ptr >& albums, int row ) PlaylistModel::insertAlbums( const QList< Tomahawk::album_ptr >& albums, int row )
{ {
Q_D( PlaylistModel );
// FIXME: This currently appends, not inserts! // FIXME: This currently appends, not inserts!
Q_UNUSED( row ); Q_UNUSED( row );
@@ -183,7 +198,7 @@ PlaylistModel::insertAlbums( const QList< Tomahawk::album_ptr >& albums, int row
{ {
setTitle( albums.first()->name() ); setTitle( albums.first()->name() );
setDescription( tr( "All tracks by %1 on album %2" ).arg( albums.first()->artist()->name() ).arg( albums.first()->name() ) ); setDescription( tr( "All tracks by %1 on album %2" ).arg( albums.first()->artist()->name() ).arg( albums.first()->name() ) );
m_isTemporary = true; d->isTemporary = true;
} }
} }
@@ -191,6 +206,7 @@ PlaylistModel::insertAlbums( const QList< Tomahawk::album_ptr >& albums, int row
void void
PlaylistModel::insertArtists( const QList< Tomahawk::artist_ptr >& artists, int row ) PlaylistModel::insertArtists( const QList< Tomahawk::artist_ptr >& artists, int row )
{ {
Q_D( PlaylistModel );
// FIXME: This currently appends, not inserts! // FIXME: This currently appends, not inserts!
Q_UNUSED( row ); Q_UNUSED( row );
@@ -209,7 +225,7 @@ PlaylistModel::insertArtists( const QList< Tomahawk::artist_ptr >& artists, int
{ {
setTitle( artists.first()->name() ); setTitle( artists.first()->name() );
setDescription( tr( "All tracks by %1" ).arg( artists.first()->name() ) ); setDescription( tr( "All tracks by %1" ).arg( artists.first()->name() ) );
m_isTemporary = true; d->isTemporary = true;
} }
} }
@@ -217,10 +233,11 @@ PlaylistModel::insertArtists( const QList< Tomahawk::artist_ptr >& artists, int
void void
PlaylistModel::insertQueries( const QList< Tomahawk::query_ptr >& queries, int row, const QList< Tomahawk::PlaybackLog >& logs ) PlaylistModel::insertQueries( const QList< Tomahawk::query_ptr >& queries, int row, const QList< Tomahawk::PlaybackLog >& logs )
{ {
Q_D( PlaylistModel );
QList< Tomahawk::plentry_ptr > entries; QList< Tomahawk::plentry_ptr > entries;
foreach ( const query_ptr& query, queries ) foreach ( const query_ptr& query, queries )
{ {
if ( m_acceptPlayableQueriesOnly && query && query->resolvingFinished() && !query->playable() ) if ( d->acceptPlayableQueriesOnly && query && query->resolvingFinished() && !query->playable() )
continue; continue;
plentry_ptr entry = plentry_ptr( new PlaylistEntry() ); plentry_ptr entry = plentry_ptr( new PlaylistEntry() );
@@ -245,6 +262,7 @@ PlaylistModel::insertQueries( const QList< Tomahawk::query_ptr >& queries, int r
void void
PlaylistModel::insertEntries( const QList< Tomahawk::plentry_ptr >& entries, int row, const QList< Tomahawk::PlaybackLog >& logs ) PlaylistModel::insertEntries( const QList< Tomahawk::plentry_ptr >& entries, int row, const QList< Tomahawk::PlaybackLog >& logs )
{ {
Q_D( PlaylistModel );
if ( !entries.count() ) if ( !entries.count() )
{ {
emit itemCountChanged( rowCount( QModelIndex() ) ); emit itemCountChanged( rowCount( QModelIndex() ) );
@@ -257,10 +275,10 @@ PlaylistModel::insertEntries( const QList< Tomahawk::plentry_ptr >& entries, int
crows.first = c; crows.first = c;
crows.second = c + entries.count() - 1; crows.second = c + entries.count() - 1;
if ( !m_isLoading ) if ( !d->isLoading )
{ {
m_savedInsertPos = row; d->savedInsertPos = row;
m_savedInsertTracks = entries; d->savedInsertTracks = entries;
} }
emit beginInsertRows( QModelIndex(), crows.first, crows.second ); emit beginInsertRows( QModelIndex(), crows.first, crows.second );
@@ -284,14 +302,14 @@ PlaylistModel::insertEntries( const QList< Tomahawk::plentry_ptr >& entries, int
if ( !entry->query()->resolvingFinished() && !entry->query()->playable() ) if ( !entry->query()->resolvingFinished() && !entry->query()->playable() )
{ {
queries << entry->query(); queries << entry->query();
m_waitingForResolved.append( entry->query().data() ); d->waitingForResolved.append( entry->query().data() );
connect( entry->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( trackResolved( bool ) ) ); connect( entry->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( trackResolved( bool ) ) );
} }
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) ); connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
} }
if ( !m_waitingForResolved.isEmpty() ) if ( !d->waitingForResolved.isEmpty() )
{ {
Pipeline::instance()->resolve( queries ); Pipeline::instance()->resolve( queries );
emit loadingStarted(); emit loadingStarted();
@@ -307,6 +325,7 @@ PlaylistModel::insertEntries( const QList< Tomahawk::plentry_ptr >& entries, int
void void
PlaylistModel::trackResolved( bool ) PlaylistModel::trackResolved( bool )
{ {
Q_D( PlaylistModel );
Tomahawk::Query* q = qobject_cast< Query* >( sender() ); Tomahawk::Query* q = qobject_cast< Query* >( sender() );
if ( !q ) if ( !q )
{ {
@@ -314,13 +333,13 @@ PlaylistModel::trackResolved( bool )
return; return;
} }
if ( m_waitingForResolved.contains( q ) ) if ( d->waitingForResolved.contains( q ) )
{ {
m_waitingForResolved.removeAll( q ); d->waitingForResolved.removeAll( q );
disconnect( q, SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolved( bool ) ) ); disconnect( q, SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolved( bool ) ) );
} }
if ( m_waitingForResolved.isEmpty() ) if ( d->waitingForResolved.isEmpty() )
{ {
emit loadingFinished(); emit loadingFinished();
} }
@@ -330,28 +349,31 @@ PlaylistModel::trackResolved( bool )
void void
PlaylistModel::onRevisionLoaded( Tomahawk::PlaylistRevision revision ) PlaylistModel::onRevisionLoaded( Tomahawk::PlaylistRevision revision )
{ {
if ( !m_waitForRevision.contains( revision.revisionguid ) ) Q_D( PlaylistModel );
loadPlaylist( m_playlist ); if ( !d->waitForRevision.contains( revision.revisionguid ) )
loadPlaylist( d->playlist );
else else
m_waitForRevision.removeAll( revision.revisionguid ); d->waitForRevision.removeAll( revision.revisionguid );
} }
QMimeData* QMimeData*
PlaylistModel::mimeData( const QModelIndexList& indexes ) const PlaylistModel::mimeData( const QModelIndexList& indexes ) const
{ {
Q_D( const PlaylistModel );
// Add the playlist id to the mime data so that we can detect dropping on ourselves // Add the playlist id to the mime data so that we can detect dropping on ourselves
QMimeData* d = PlayableModel::mimeData( indexes ); QMimeData* data = PlayableModel::mimeData( indexes );
if ( !m_playlist.isNull() ) if ( !d->playlist.isNull() )
d->setData( "application/tomahawk.playlist.id", m_playlist->guid().toLatin1() ); data->setData( "application/tomahawk.playlist.id", d->playlist->guid().toLatin1() );
return d; return data;
} }
bool bool
PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
{ {
Q_D( PlaylistModel );
Q_UNUSED( column ); Q_UNUSED( column );
if ( action == Qt::IgnoreAction || isReadOnly() ) if ( action == Qt::IgnoreAction || isReadOnly() )
@@ -360,9 +382,9 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
if ( !DropJob::acceptsMimeData( data ) ) if ( !DropJob::acceptsMimeData( data ) )
return false; return false;
m_dropStorage.row = row; d->dropStorage.row = row;
m_dropStorage.parent = QPersistentModelIndex( parent ); d->dropStorage.parent = QPersistentModelIndex( parent );
m_dropStorage.action = action; d->dropStorage.action = action;
DropJob* dj = new DropJob(); DropJob* dj = new DropJob();
@@ -378,7 +400,7 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
// On mac, drags from outside the app are still Qt::MoveActions instead of Qt::CopyAction by default // On mac, drags from outside the app are still Qt::MoveActions instead of Qt::CopyAction by default
// so check if the drag originated in this playlist to determine whether or not to copy // so check if the drag originated in this playlist to determine whether or not to copy
if ( !data->hasFormat( "application/tomahawk.playlist.id" ) || if ( !data->hasFormat( "application/tomahawk.playlist.id" ) ||
( !m_playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) ) ( !d->playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != d->playlist->guid() ) )
{ {
dj->setDropAction( DropJob::Append ); dj->setDropAction( DropJob::Append );
} }
@@ -394,66 +416,70 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
playlist_ptr playlist_ptr
PlaylistModel::playlist() const PlaylistModel::playlist() const
{ {
return m_playlist; Q_D( const PlaylistModel );
return d->playlist;
} }
void void
PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks ) PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks )
{ {
if ( m_dropStorage.row == -10 ) // nope Q_D( PlaylistModel );
if ( d->dropStorage.row == -10 ) // nope
return; return;
int beginRow; int beginRow;
if ( m_dropStorage.row != -1 ) if ( d->dropStorage.row != -1 )
beginRow = m_dropStorage.row; beginRow = d->dropStorage.row;
else if ( m_dropStorage.parent.isValid() ) else if ( d->dropStorage.parent.isValid() )
beginRow = m_dropStorage.parent.row(); beginRow = d->dropStorage.parent.row();
else else
beginRow = rowCount( QModelIndex() ); beginRow = rowCount( QModelIndex() );
if ( tracks.count() ) if ( tracks.count() )
{ {
bool update = ( m_dropStorage.action & Qt::CopyAction || m_dropStorage.action & Qt::MoveAction ); bool update = ( d->dropStorage.action & Qt::CopyAction || d->dropStorage.action & Qt::MoveAction );
if ( update ) if ( update )
beginPlaylistChanges(); beginPlaylistChanges();
insertQueries( tracks, beginRow ); insertQueries( tracks, beginRow );
if ( update && m_dropStorage.action & Qt::CopyAction ) if ( update && d->dropStorage.action & Qt::CopyAction )
endPlaylistChanges(); endPlaylistChanges();
} }
m_dropStorage.parent = QPersistentModelIndex(); d->dropStorage.parent = QPersistentModelIndex();
m_dropStorage.row = -10; d->dropStorage.row = -10;
} }
void void
PlaylistModel::beginPlaylistChanges() PlaylistModel::beginPlaylistChanges()
{ {
if ( m_playlist.isNull() || !m_playlist->author()->isLocal() ) Q_D( PlaylistModel );
if ( d->playlist.isNull() || !d->playlist->author()->isLocal() )
return; return;
Q_ASSERT( !m_changesOngoing ); Q_ASSERT( !d->changesOngoing );
m_changesOngoing = true; d->changesOngoing = true;
} }
void void
PlaylistModel::endPlaylistChanges() PlaylistModel::endPlaylistChanges()
{ {
if ( m_playlist.isNull() || !m_playlist->author()->isLocal() ) Q_D( PlaylistModel );
if ( d->playlist.isNull() || !d->playlist->author()->isLocal() )
{ {
m_savedInsertPos = -1; d->savedInsertPos = -1;
m_savedInsertTracks.clear(); d->savedInsertTracks.clear();
m_savedRemoveTracks.clear(); d->savedRemoveTracks.clear();
return; return;
} }
if ( m_changesOngoing ) if ( d->changesOngoing )
{ {
m_changesOngoing = false; d->changesOngoing = false;
} }
else else
{ {
@@ -463,9 +489,9 @@ PlaylistModel::endPlaylistChanges()
QList<plentry_ptr> l = playlistEntries(); QList<plentry_ptr> l = playlistEntries();
QString newrev = uuid(); QString newrev = uuid();
m_waitForRevision << newrev; d->waitForRevision << newrev;
if ( dynplaylist_ptr dynplaylist = m_playlist.dynamicCast<Tomahawk::DynamicPlaylist>() ) if ( dynplaylist_ptr dynplaylist = d->playlist.dynamicCast<Tomahawk::DynamicPlaylist>() )
{ {
if ( dynplaylist->mode() == OnDemand ) if ( dynplaylist->mode() == OnDemand )
{ {
@@ -478,11 +504,11 @@ PlaylistModel::endPlaylistChanges()
} }
else else
{ {
m_playlist->createNewRevision( newrev, m_playlist->currentrevision(), l ); d->playlist->createNewRevision( newrev, d->playlist->currentrevision(), l );
} }
if ( m_savedInsertPos >= 0 && !m_savedInsertTracks.isEmpty() && if ( d->savedInsertPos >= 0 && !d->savedInsertTracks.isEmpty() &&
!m_savedRemoveTracks.isEmpty() ) !d->savedRemoveTracks.isEmpty() )
{ {
// If we have *both* an insert and remove, then it's a move action // If we have *both* an insert and remove, then it's a move action
// However, since we got the insert before the remove (Qt...), the index we have as the saved // However, since we got the insert before the remove (Qt...), the index we have as the saved
@@ -498,27 +524,27 @@ PlaylistModel::endPlaylistChanges()
continue; continue;
// qDebug() << "Checking for equality:" << (item->entry() == m_savedInsertTracks.first()) << m_savedInsertTracks.first()->query()->track() << m_savedInsertTracks.first()->query()->artist(); // qDebug() << "Checking for equality:" << (item->entry() == m_savedInsertTracks.first()) << m_savedInsertTracks.first()->query()->track() << m_savedInsertTracks.first()->query()->artist();
if ( item->entry() == m_savedInsertTracks.first() ) if ( item->entry() == d->savedInsertTracks.first() )
{ {
// Found our index // Found our index
emit m_playlist->tracksMoved( m_savedInsertTracks, i ); emit d->playlist->tracksMoved( d->savedInsertTracks, i );
break; break;
} }
} }
m_savedInsertPos = -1; d->savedInsertPos = -1;
m_savedInsertTracks.clear(); d->savedInsertTracks.clear();
m_savedRemoveTracks.clear(); d->savedRemoveTracks.clear();
} }
else if ( m_savedInsertPos >= 0 ) else if ( d->savedInsertPos >= 0 )
{ {
emit m_playlist->tracksInserted( m_savedInsertTracks, m_savedInsertPos ); emit d->playlist->tracksInserted( d->savedInsertTracks, d->savedInsertPos );
m_savedInsertPos = -1; d->savedInsertPos = -1;
m_savedInsertTracks.clear(); d->savedInsertTracks.clear();
} }
else if ( !m_savedRemoveTracks.isEmpty() ) else if ( !d->savedRemoveTracks.isEmpty() )
{ {
emit m_playlist->tracksRemoved( m_savedRemoveTracks ); emit d->playlist->tracksRemoved( d->savedRemoveTracks );
m_savedRemoveTracks.clear(); d->savedRemoveTracks.clear();
} }
} }
@@ -545,21 +571,22 @@ PlaylistModel::playlistEntries() const
void void
PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome )
{ {
Q_D( PlaylistModel );
PlayableItem* item = itemFromIndex( index ); PlayableItem* item = itemFromIndex( index );
if ( item && m_waitingForResolved.contains( item->query().data() ) ) if ( item && d->waitingForResolved.contains( item->query().data() ) )
{ {
disconnect( item->query().data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolved( bool ) ) ); disconnect( item->query().data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( trackResolved( bool ) ) );
m_waitingForResolved.removeAll( item->query().data() ); d->waitingForResolved.removeAll( item->query().data() );
if ( m_waitingForResolved.isEmpty() ) if ( d->waitingForResolved.isEmpty() )
emit loadingFinished(); emit loadingFinished();
} }
if ( !m_changesOngoing ) if ( !d->changesOngoing )
beginPlaylistChanges(); beginPlaylistChanges();
if ( item && !m_isLoading ) if ( item && !d->isLoading )
m_savedRemoveTracks << item->query(); d->savedRemoveTracks << item->query();
PlayableModel::removeIndex( index, moreToCome ); PlayableModel::removeIndex( index, moreToCome );
@@ -571,33 +598,38 @@ PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome )
bool bool
PlaylistModel::waitForRevision( const QString& revisionguid ) const PlaylistModel::waitForRevision( const QString& revisionguid ) const
{ {
return m_waitForRevision.contains( revisionguid ); Q_D( const PlaylistModel );
return d->waitForRevision.contains( revisionguid );
} }
void void
PlaylistModel::removeFromWaitList( const QString& revisionguid ) PlaylistModel::removeFromWaitList( const QString& revisionguid )
{ {
m_waitForRevision.removeAll( revisionguid ); Q_D( PlaylistModel );
d->waitForRevision.removeAll( revisionguid );
} }
bool bool
PlaylistModel::isTemporary() const PlaylistModel::isTemporary() const
{ {
return m_isTemporary; Q_D( const PlaylistModel );
return d->isTemporary;
} }
bool bool
PlaylistModel::acceptPlayableQueriesOnly() const PlaylistModel::acceptPlayableQueriesOnly() const
{ {
return m_acceptPlayableQueriesOnly; Q_D( const PlaylistModel );
return d->acceptPlayableQueriesOnly;
} }
void void
PlaylistModel::setAcceptPlayableQueriesOnly( bool b ) PlaylistModel::setAcceptPlayableQueriesOnly( bool b )
{ {
m_acceptPlayableQueriesOnly = b; Q_D( PlaylistModel );
d->acceptPlayableQueriesOnly = b;
} }

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -16,23 +17,26 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once
#ifndef PLAYLISTMODEL_H #ifndef PLAYLISTMODEL_H
#define PLAYLISTMODEL_H #define PLAYLISTMODEL_H
#include <QList> #include <QList>
#include <QHash> #include <QHash>
#include "Typedefs.h"
#include "PlayableModel.h"
#include "Playlist.h" #include "Playlist.h"
#include "Query.h" #include "Query.h"
#include "PlaylistInterface.h" #include "PlaylistInterface.h"
#include "DllMacro.h" #include "DllMacro.h"
#include "PlayableModel.h"
#include "Typedefs.h"
class QMimeData; class QMimeData;
class QMetaData; class QMetaData;
class PlaylistModelPrivate;
class DLLEXPORT PlaylistModel : public PlayableModel class DLLEXPORT PlaylistModel : public PlayableModel
{ {
Q_OBJECT Q_OBJECT
@@ -79,6 +83,7 @@ signals:
void playlistChanged(); void playlistChanged();
protected: protected:
PlaylistModel( QObject* parent, PlaylistModelPrivate* d );
bool waitForRevision( const QString& revisionguid ) const; bool waitForRevision( const QString& revisionguid ) const;
void removeFromWaitList( const QString& revisionguid ); void removeFromWaitList( const QString& revisionguid );
@@ -93,20 +98,9 @@ private slots:
private: private:
void beginPlaylistChanges(); void beginPlaylistChanges();
void endPlaylistChanges(); void endPlaylistChanges();
void init();
Tomahawk::playlist_ptr m_playlist; Q_DECLARE_PRIVATE( PlaylistModel )
bool m_isTemporary;
bool m_changesOngoing;
bool m_isLoading;
bool m_acceptPlayableQueriesOnly;
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;
}; };
#endif // PLAYLISTMODEL_H #endif // PLAYLISTMODEL_H

View File

@@ -0,0 +1,58 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef PLAYLISTMODEL_P_H
#define PLAYLISTMODEL_P_H
#include "PlaylistModel.h"
#include "PlayableModel_p.h"
class PlaylistModelPrivate : public PlayableModelPrivate
{
public:
PlaylistModelPrivate( PlaylistModel* q )
: PlayableModelPrivate( q, false )
, isTemporary( false )
, changesOngoing( false )
, isLoading( false )
, acceptPlayableQueriesOnly( false )
, savedInsertPos( -1 )
{
}
Q_DECLARE_PUBLIC( PlaylistModel )
private:
Tomahawk::playlist_ptr playlist;
bool isTemporary;
bool changesOngoing;
bool isLoading;
bool acceptPlayableQueriesOnly;
QList< Tomahawk::Query* > waitingForResolved;
QStringList waitForRevision;
int savedInsertPos;
QList< Tomahawk::plentry_ptr > savedInsertTracks;
QList< Tomahawk::query_ptr > savedRemoveTracks;
PlaylistModel::DropStorageData dropStorage;
};
#endif // PLAYLISTMODEL_P_H

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
* *
* Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -16,37 +17,18 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "TopLovedTracksModel.h" #include "TopLovedTracksModel_p.h"
#include <QMimeData>
#include <QTreeView>
#include "database/Database.h" #include "database/Database.h"
#include "database/DatabaseCommand_GenericSelect.h" #include "database/DatabaseCommand_GenericSelect.h"
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
#include "PlayableItem.h"
#include "PlaylistEntry.h"
#include "Source.h" #include "Source.h"
#include "SourceList.h"
#include <QTimer>
#define LOVED_TRACK_ITEMS 25
using namespace Tomahawk; using namespace Tomahawk;
TopLovedTracksModel::TopLovedTracksModel( QObject* parent ) TopLovedTracksModel::TopLovedTracksModel( QObject* parent )
: PlaylistModel( parent ) : LovedTracksModel( parent )
, m_smoothingTimer( new QTimer )
, m_limit( LOVED_TRACK_ITEMS )
{ {
m_smoothingTimer->setInterval( 300 );
m_smoothingTimer->setSingleShot( true );
connect( m_smoothingTimer, SIGNAL( timeout() ), this, SLOT( loadTracks() ) );
} }
@@ -55,33 +37,21 @@ TopLovedTracksModel::~TopLovedTracksModel()
} }
unsigned int
TopLovedTracksModel::limit() const
{
return m_limit;
}
void
TopLovedTracksModel::setLimit( unsigned int limit )
{
m_limit = limit;
}
void void
TopLovedTracksModel::loadTracks() TopLovedTracksModel::loadTracks()
{ {
Q_D( TopLovedTracksModel );
startLoading(); startLoading();
QString sql; QString sql;
if ( m_source.isNull() ) if ( d->source.isNull() )
{ {
sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter " sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter "
"FROM social_attributes, track, artist " "FROM social_attributes, track, artist "
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' " "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' "
"GROUP BY track.id " "GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC LIMIT 0, 50" ); "ORDER BY counter DESC, social_attributes.timestamp DESC LIMIT %1" )
.arg( d->limit );
} }
else else
{ {
@@ -89,87 +59,12 @@ TopLovedTracksModel::loadTracks()
"FROM social_attributes, track, artist " "FROM social_attributes, track, artist "
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' AND social_attributes.source %1 " "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' AND social_attributes.source %1 "
"GROUP BY track.id " "GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) ); "ORDER BY counter DESC, social_attributes.timestamp DESC "
)
.arg( d->source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( d->source->id() ) );
} }
DatabaseCommand_GenericSelect* cmd = new DatabaseCommand_GenericSelect( sql, DatabaseCommand_GenericSelect::Track, -1, 0 ); DatabaseCommand_GenericSelect* cmd = new DatabaseCommand_GenericSelect( sql, DatabaseCommand_GenericSelect::Track, -1, 0 );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksLoaded( QList<Tomahawk::query_ptr> ) ) ); connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksLoaded( QList<Tomahawk::query_ptr> ) ) );
Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) ); Database::instance()->enqueue( Tomahawk::dbcmd_ptr( cmd ) );
} }
void
TopLovedTracksModel::onSourcesReady()
{
Q_ASSERT( m_source.isNull() );
loadTracks();
foreach ( const source_ptr& source, SourceList::instance()->sources() )
onSourceAdded( source );
}
void
TopLovedTracksModel::setSource( const Tomahawk::source_ptr& source )
{
m_source = source;
if ( source.isNull() )
{
if ( SourceList::instance()->isReady() )
onSourcesReady();
else
connect( SourceList::instance(), SIGNAL( ready() ), SLOT( onSourcesReady() ) );
connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) );
}
else
{
onSourceAdded( source );
loadTracks();
}
}
void
TopLovedTracksModel::onSourceAdded( const Tomahawk::source_ptr& source )
{
connect( source.data(), SIGNAL( socialAttributesChanged( QString ) ), SLOT( onTrackLoved() ), Qt::UniqueConnection );
}
void
TopLovedTracksModel::onTrackLoved()
{
m_smoothingTimer->start();
}
bool
TopLovedTracksModel::isTemporary() const
{
return true;
}
void
TopLovedTracksModel::tracksLoaded( QList< query_ptr > newLoved )
{
finishLoading();
QList< query_ptr > tracks;
foreach ( const plentry_ptr ple, playlistEntries() )
tracks << ple->query();
bool changed = false;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, newLoved, changed );
if ( changed )
{
QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
clear();
appendEntries( el );
}
}

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
* *
* Tomahawk is free software: you can redistribute it and/or modify * Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -16,18 +17,15 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef LOVEDTRACKSMODEL_H #pragma once
#define LOVEDTRACKSMODEL_H #ifndef TOPLOVEDTRACKSMODEL_H
#define TOPLOVEDTRACKSMODEL_H
#include <QList> #include "LovedTracksModel.h"
#include <QHash>
#include "Typedefs.h" class TopLovedTracksModelPrivate;
#include "PlaylistModel.h"
#include "DllMacro.h" class DLLEXPORT TopLovedTracksModel : public LovedTracksModel
class DLLEXPORT TopLovedTracksModel : public PlaylistModel
{ {
Q_OBJECT Q_OBJECT
@@ -35,26 +33,11 @@ public:
explicit TopLovedTracksModel( QObject* parent = 0 ); explicit TopLovedTracksModel( QObject* parent = 0 );
virtual ~TopLovedTracksModel(); virtual ~TopLovedTracksModel();
unsigned int limit() const;
void setLimit( unsigned int limit );
bool isTemporary() const;
public slots:
void setSource( const Tomahawk::source_ptr& source );
private slots: private slots:
void onSourcesReady();
void onSourceAdded( const Tomahawk::source_ptr& source );
void onTrackLoved();
void loadTracks(); void loadTracks();
void tracksLoaded( QList<Tomahawk::query_ptr> );
private: private:
Tomahawk::source_ptr m_source; Q_DECLARE_PRIVATE( TopLovedTracksModel )
QTimer* m_smoothingTimer;
unsigned int m_limit;
}; };
#endif // LOVEDTRACKSMODEL_H #endif // TOPLOVEDTRACKSMODEL_H

View File

@@ -0,0 +1,38 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef TOPLOVEDTRACKSMODEL_P_H
#define TOPLOVEDTRACKSMODEL_P_H
#include "LovedTracksModel_p.h"
#include "TopLovedTracksModel.h"
class TopLovedTracksModelPrivate : public LovedTracksModelPrivate
{
public:
TopLovedTracksModelPrivate( TopLovedTracksModel* q )
: LovedTracksModelPrivate( q )
{
}
Q_DECLARE_PUBLIC( TopLovedTracksModel )
};
#endif // TOPLOVEDTRACKSMODEL_P_H